[libc-commits] [libc] [libc] Implement lit-based test execution for Libc (PR #178746)

Jeff Bailey via libc-commits libc-commits at lists.llvm.org
Thu Feb 12 09:47:03 PST 2026


https://github.com/kaladron updated https://github.com/llvm/llvm-project/pull/178746

>From 49dc9b52b1fbeb4cdcfa1c84813f59a7178d8b4d Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jeffbailey at google.com>
Date: Mon, 19 Jan 2026 14:43:58 +0000
Subject: [PATCH 1/2] [libc] Implement lit-based test execution for Libc

This provides optional lit-based test execution for the LLVM
Libc tests, alongside the existing CMake-based test execution.

Usage:
  ninja -C build check-libc-lit
  cd build && bin/llvm-lit libc/test/src/
---
 libc/cmake/modules/LLVMLibCLitTestRules.cmake | 85 ++++++++++++++++
 libc/cmake/modules/LLVMLibCRules.cmake        |  1 +
 libc/cmake/modules/LLVMLibCTestRules.cmake    |  8 ++
 libc/test/CMakeLists.txt                      | 13 +++
 libc/test/lit.cfg.py                          | 49 ++++++++++
 libc/test/lit.site.cfg.py.in                  | 23 +++++
 libc/utils/LibcTestFormat/__init__.py         | 21 ++++
 libc/utils/LibcTestFormat/format.py           | 96 +++++++++++++++++++
 8 files changed, 296 insertions(+)
 create mode 100644 libc/cmake/modules/LLVMLibCLitTestRules.cmake
 create mode 100644 libc/test/lit.cfg.py
 create mode 100644 libc/test/lit.site.cfg.py.in
 create mode 100644 libc/utils/LibcTestFormat/__init__.py
 create mode 100644 libc/utils/LibcTestFormat/format.py

diff --git a/libc/cmake/modules/LLVMLibCLitTestRules.cmake b/libc/cmake/modules/LLVMLibCLitTestRules.cmake
new file mode 100644
index 0000000000000..bfe59239a1b51
--- /dev/null
+++ b/libc/cmake/modules/LLVMLibCLitTestRules.cmake
@@ -0,0 +1,85 @@
+#===============================================================================
+# Lit Test Infrastructure for LLVM libc
+#
+# This module provides functions to set up lit-based testing for libc.
+#
+# Lit discovers and runs the test executables created by the
+# add_libc_test() infrastructure. No separate build rules are needed.
+#===============================================================================
+
+# Guard against double inclusion
+if(LIBC_LIT_TEST_RULES_INCLUDED)
+  return()
+endif()
+set(LIBC_LIT_TEST_RULES_INCLUDED TRUE)
+
+# Include LLVM's lit infrastructure
+include(AddLLVM)
+
+#-------------------------------------------------------------------------------
+# configure_libc_lit_site_cfg()
+#
+# Configures the lit.site.cfg.py file from its template.
+# This should be called once from libc/test/CMakeLists.txt.
+#-------------------------------------------------------------------------------
+function(configure_libc_lit_site_cfg)
+  # Set variables for the template
+  set(LIBC_SOURCE_DIR "${LIBC_SOURCE_DIR}")
+  set(LIBC_BINARY_DIR "${LIBC_BUILD_DIR}")
+  
+  # Configure the site config file
+  configure_lit_site_cfg(
+    ${LIBC_SOURCE_DIR}/test/lit.site.cfg.py.in
+    ${LIBC_BUILD_DIR}/test/lit.site.cfg.py
+    MAIN_CONFIG
+    ${LIBC_SOURCE_DIR}/test/lit.cfg.py
+    PATHS
+    "LLVM_SOURCE_DIR"
+    "LLVM_BINARY_DIR"
+    "LLVM_TOOLS_DIR"
+    "LLVM_LIBS_DIR"
+    "LIBC_SOURCE_DIR"
+    "LIBC_BINARY_DIR"
+  )
+endfunction()
+
+#-------------------------------------------------------------------------------
+# add_libc_lit_testsuite()
+#
+# Creates a lit test suite target for a specific test directory.
+#
+# Usage:
+#   add_libc_lit_testsuite(check-libc-ctype
+#     SUITE_NAME ctype
+#     TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/ctype
+#     DEPENDS libc-ctype-tests
+#   )
+#
+# Note: TEST_DIR should be the build directory where test executables are
+# located, not the source directory.
+#-------------------------------------------------------------------------------
+function(add_libc_lit_testsuite target_name)
+  cmake_parse_arguments(
+    "LIT_SUITE"
+    ""                    # No optional arguments
+    "SUITE_NAME;TEST_DIR" # Single value arguments
+    "DEPENDS"             # Multi value arguments
+    ${ARGN}
+  )
+  
+  if(NOT LIT_SUITE_TEST_DIR)
+    message(FATAL_ERROR "add_libc_lit_testsuite requires TEST_DIR")
+  endif()
+  
+  # Create the lit test target using LLVM's infrastructure
+  add_lit_testsuite(${target_name}
+    "Running ${LIT_SUITE_SUITE_NAME} libc tests"
+    ${LIT_SUITE_TEST_DIR}
+    DEPENDS ${LIT_SUITE_DEPENDS}
+  )
+  
+  # Add to the umbrella check-libc-lit target if it exists
+  if(TARGET check-libc-lit)
+    add_dependencies(check-libc-lit ${target_name})
+  endif()
+endfunction()
diff --git a/libc/cmake/modules/LLVMLibCRules.cmake b/libc/cmake/modules/LLVMLibCRules.cmake
index 76f3892eeb32e..029ecc390128d 100644
--- a/libc/cmake/modules/LLVMLibCRules.cmake
+++ b/libc/cmake/modules/LLVMLibCRules.cmake
@@ -4,3 +4,4 @@ include(LLVMLibCFlagRules)
 include(LLVMLibCObjectRules)
 include(LLVMLibCLibraryRules)
 include(LLVMLibCTestRules)
+include(LLVMLibCLitTestRules)
diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake
index ba64933708760..e59f5447c5a0f 100644
--- a/libc/cmake/modules/LLVMLibCTestRules.cmake
+++ b/libc/cmake/modules/LLVMLibCTestRules.cmake
@@ -344,6 +344,10 @@ function(create_libc_unittest fq_target_name)
     )
   endif()
   add_dependencies(libc-unit-tests ${fq_target_name})
+  # Also add dependency to build-only target for lit
+  if(TARGET libc-unit-tests-build)
+    add_dependencies(libc-unit-tests-build ${fq_build_target_name})
+  endif()
 endfunction(create_libc_unittest)
 
 function(add_libc_unittest target_name)
@@ -883,6 +887,10 @@ function(add_libc_hermetic test_name)
     # If it is a benchmark, it will already have been added to the
     # gpu-benchmark target
     add_dependencies(libc-hermetic-tests ${fq_target_name})
+    # Also add dependency to build-only target for lit
+    if(TARGET libc-hermetic-tests-build)
+      add_dependencies(libc-hermetic-tests-build ${fq_build_target_name})
+    endif()
   endif()
 endfunction(add_libc_hermetic)
 
diff --git a/libc/test/CMakeLists.txt b/libc/test/CMakeLists.txt
index 011ad6aeb34b7..30e4c4239174e 100644
--- a/libc/test/CMakeLists.txt
+++ b/libc/test/CMakeLists.txt
@@ -6,6 +6,19 @@ add_dependencies(check-libc libc-unit-tests libc-hermetic-tests)
 add_custom_target(exhaustive-check-libc)
 add_custom_target(libc-long-running-tests)
 
+# Build-only targets for lit (don't run tests, just build executables)
+add_custom_target(libc-unit-tests-build)
+add_custom_target(libc-hermetic-tests-build)
+
+# Configure lit test infrastructure
+# Lit depends on build-only targets so tests aren't run during build
+configure_libc_lit_site_cfg()
+add_lit_testsuite(check-libc-lit
+  "Running libc tests via lit"
+  ${LIBC_BUILD_DIR}/test
+  DEPENDS libc-unit-tests-build libc-hermetic-tests-build
+)
+
 add_subdirectory(UnitTest)
 
 if(LIBC_TARGET_OS_IS_GPU)
diff --git a/libc/test/lit.cfg.py b/libc/test/lit.cfg.py
new file mode 100644
index 0000000000000..9cc9f6be446cf
--- /dev/null
+++ b/libc/test/lit.cfg.py
@@ -0,0 +1,49 @@
+# -*- Python -*-
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# ===----------------------------------------------------------------------===##
+#
+# This is the lit configuration for LLVM libc tests.
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+import site
+import sys
+
+import lit.formats
+import lit.util
+
+# Add libc's utils directory to the path so we can import the test format
+site.addsitedir(os.path.join(config.libc_src_root, "utils"))
+import LibcTestFormat
+
+# Configuration file for the 'lit' test runner.
+
+# name: The name of this test suite.
+config.name = "libc"
+
+# testFormat: Use libc's custom test format that discovers pre-built
+# test executables (Libc*Tests) in the build directory.
+config.test_format = LibcTestFormat.LibcTest()
+
+# suffixes: Not used by LibcTest format, but kept for compatibility
+config.suffixes = []
+
+# excludes: A list of directories to exclude from the testsuite.
+config.excludes = ["Inputs", "CMakeLists.txt", "README.txt", "LICENSE.txt", "UnitTest"]
+
+# test_source_root: The root path where tests are located.
+# test_exec_root: The root path where test executables are built.
+# Set both to the build directory so ExecutableTest finds executables correctly.
+config.test_exec_root = os.path.join(config.libc_obj_root, "test")
+config.test_source_root = config.test_exec_root
+
+# Add tool directories to PATH (in case we add FileCheck tests later)
+if hasattr(config, "llvm_tools_dir") and config.llvm_tools_dir:
+    config.environment["PATH"] = os.path.pathsep.join(
+        [config.llvm_tools_dir, config.environment.get("PATH", "")]
+    )
diff --git a/libc/test/lit.site.cfg.py.in b/libc/test/lit.site.cfg.py.in
new file mode 100644
index 0000000000000..b4fd7d07ba716
--- /dev/null
+++ b/libc/test/lit.site.cfg.py.in
@@ -0,0 +1,23 @@
+ at LIT_SITE_CFG_IN_HEADER@
+
+import sys
+import os
+
+# Configuration values from CMake
+config.llvm_src_root = path(r"@LLVM_SOURCE_DIR@")
+config.llvm_obj_root = path(r"@LLVM_BINARY_DIR@")
+config.llvm_tools_dir = lit_config.substitute(path(r"@LLVM_TOOLS_DIR@"))
+config.llvm_libs_dir = lit_config.substitute(path(r"@LLVM_LIBS_DIR@"))
+config.libc_src_root = path(r"@LIBC_SOURCE_DIR@")
+config.libc_obj_root = path(r"@LIBC_BINARY_DIR@")
+config.target_triple = "@LLVM_TARGET_TRIPLE@"
+config.host_triple = "@LLVM_HOST_TRIPLE@"
+config.python_executable = "@Python3_EXECUTABLE@"
+
+# Initialize lit.llvm module
+import lit.llvm
+lit.llvm.initialize(lit_config, config)
+
+# Let the main config do the real work.
+lit_config.load_config(
+    config, os.path.join(config.libc_src_root, "test/lit.cfg.py"))
diff --git a/libc/utils/LibcTestFormat/__init__.py b/libc/utils/LibcTestFormat/__init__.py
new file mode 100644
index 0000000000000..9472aa94e7f30
--- /dev/null
+++ b/libc/utils/LibcTestFormat/__init__.py
@@ -0,0 +1,21 @@
+# ===----------------------------------------------------------------------===##
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# ===----------------------------------------------------------------------===##
+
+"""
+Lit test format for LLVM libc unit tests.
+
+This format extends lit.formats.ExecutableTest to discover pre-built test
+executables in the build directory. Test executables are expected to follow
+the naming pattern used by add_libc_test():
+  libc.test.src.<category>.<test_name>.__unit__.__build__
+  libc.test.src.<category>.<test_name>.__hermetic__.__build__
+"""
+
+from .format import LibcTest
+
+__all__ = ["LibcTest"]
diff --git a/libc/utils/LibcTestFormat/format.py b/libc/utils/LibcTestFormat/format.py
new file mode 100644
index 0000000000000..8b641f169e243
--- /dev/null
+++ b/libc/utils/LibcTestFormat/format.py
@@ -0,0 +1,96 @@
+# ===----------------------------------------------------------------------===##
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# ===----------------------------------------------------------------------===##
+
+"""
+Lit test format for LLVM libc tests.
+
+This format discovers pre-built test executables in the build directory
+and runs them. It extends lit's ExecutableTest format.
+
+The lit config sets test_source_root == test_exec_root (both to the build
+directory), following the pattern used by llvm/test/Unit/lit.cfg.py.
+
+Test executables are discovered by looking for files matching:
+  libc.test.src.<category>.<test_name>.__unit__.__build__
+  libc.test.src.<category>.<test_name>.__hermetic__.__build__
+
+These are created by the add_libc_test() infrastructure.
+"""
+
+import os
+
+import lit.formats
+import lit.Test
+import lit.util
+
+
+class LibcTest(lit.formats.ExecutableTest):
+    """
+    Test format for libc unit tests.
+
+    Extends ExecutableTest to discover tests from the build directory
+    rather than the source directory. Test executables are named like:
+      libc.test.src.ctype.isalnum_test.__unit__.__build__
+    and return 0 on success.
+    """
+
+    def getTestsInDirectory(self, testSuite, path_in_suite, litConfig, localConfig):
+        """
+        Discover test executables in the build directory.
+
+        Since test_source_root == test_exec_root (both point to build dir),
+        we use getSourcePath() to find test executables.
+        """
+        source_path = testSuite.getSourcePath(path_in_suite)
+
+        # Look for test executables in the build directory
+        if not os.path.isdir(source_path):
+            return
+
+        for filename in os.listdir(source_path):
+            filepath = os.path.join(source_path, filename)
+
+            # Match our test executable pattern
+            if self._isTestExecutable(filename, filepath):
+                # Create a test with the executable name
+                yield lit.Test.Test(testSuite, path_in_suite + (filename,), localConfig)
+
+    def _isTestExecutable(self, filename, filepath):
+        """Check if a file is a test executable we should run."""
+        # Pattern: libc.test.src.*.__unit__.__build__ or .__hermetic__.__build__
+        if not filename.startswith("libc.test."):
+            return False
+        if not (
+            filename.endswith(".__unit__.__build__")
+            or filename.endswith(".__hermetic__.__build__")
+        ):
+            return False
+        # Must be executable
+        if not os.path.isfile(filepath):
+            return False
+        if not os.access(filepath, os.X_OK):
+            return False
+        return True
+
+    def execute(self, test, litConfig):
+        """
+        Execute a test by running the test executable.
+
+        Runs from the executable's directory so relative paths (like
+        testdata/test.txt) work correctly.
+        """
+
+        testPath = test.getSourcePath()
+        execDir = os.path.dirname(testPath)
+
+        out, err, exitCode = lit.util.executeCommand(testPath, cwd=execDir)
+
+        if not exitCode:
+            return lit.Test.PASS, ""
+
+        return lit.Test.FAIL, out + err

>From de2b51f4266e716bbc14ce8a32bd863e6cfd9af4 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jeffbailey at google.com>
Date: Fri, 30 Jan 2026 17:31:36 +0000
Subject: [PATCH 2/2] [libc] Refactor lit configuration and add executor
 support

Streamline the lit test infrastructure for `libc` by simplifying the
CMake logic, cleaning up the configuration files, and adding support for
test executors (e.g., QEMU).

Key changes:
- **Simplified CMake**: Inlined lit configuration logic into `libc/test/CMakeLists.txt`
  and removed the separate `LLVMLibCLitTestRules.cmake` module.
- **Cleaned up lit configs**: Removed redundant configuration values and the
  `lit.llvm.initialize()` call from `lit.site.cfg.py.in`.
- **Renamed test format**: Moved `LibcTestFormat` to `libctest` in `libc/utils/`
  to follow Pythonic naming conventions while maintaining the `site.addsitedir`
  pattern used by other LLVM runtimes.
- **Added executor support**: Introduced `LIBC_TEST_CMD` to allow tests to be
  run via an external command. The `@BINARY@` placeholder in the command string
  is automatically replaced with the path to the test executable.
- **Canary Guard**: Updated `libc/test/lit.cfg.py` to prevent direct execution
  of lit in the source tree, ensuring users go through `llvm-lit` in the build
  directory.

These changes address feedback from @boomanaiden154, @petrhosek, @michaelrj-google, and @voltur01.
---
 libc/cmake/modules/LLVMLibCLitTestRules.cmake | 85 -------------------
 libc/cmake/modules/LLVMLibCRules.cmake        |  1 -
 libc/test/CMakeLists.txt                      | 18 +++-
 libc/test/lit.cfg.py                          | 52 +++---------
 libc/test/lit.site.cfg.py.in                  | 46 ++++++----
 .../{LibcTestFormat => libctest}/__init__.py  |  0
 .../{LibcTestFormat => libctest}/format.py    | 20 +++--
 7 files changed, 70 insertions(+), 152 deletions(-)
 delete mode 100644 libc/cmake/modules/LLVMLibCLitTestRules.cmake
 rename libc/utils/{LibcTestFormat => libctest}/__init__.py (100%)
 rename libc/utils/{LibcTestFormat => libctest}/format.py (81%)

diff --git a/libc/cmake/modules/LLVMLibCLitTestRules.cmake b/libc/cmake/modules/LLVMLibCLitTestRules.cmake
deleted file mode 100644
index bfe59239a1b51..0000000000000
--- a/libc/cmake/modules/LLVMLibCLitTestRules.cmake
+++ /dev/null
@@ -1,85 +0,0 @@
-#===============================================================================
-# Lit Test Infrastructure for LLVM libc
-#
-# This module provides functions to set up lit-based testing for libc.
-#
-# Lit discovers and runs the test executables created by the
-# add_libc_test() infrastructure. No separate build rules are needed.
-#===============================================================================
-
-# Guard against double inclusion
-if(LIBC_LIT_TEST_RULES_INCLUDED)
-  return()
-endif()
-set(LIBC_LIT_TEST_RULES_INCLUDED TRUE)
-
-# Include LLVM's lit infrastructure
-include(AddLLVM)
-
-#-------------------------------------------------------------------------------
-# configure_libc_lit_site_cfg()
-#
-# Configures the lit.site.cfg.py file from its template.
-# This should be called once from libc/test/CMakeLists.txt.
-#-------------------------------------------------------------------------------
-function(configure_libc_lit_site_cfg)
-  # Set variables for the template
-  set(LIBC_SOURCE_DIR "${LIBC_SOURCE_DIR}")
-  set(LIBC_BINARY_DIR "${LIBC_BUILD_DIR}")
-  
-  # Configure the site config file
-  configure_lit_site_cfg(
-    ${LIBC_SOURCE_DIR}/test/lit.site.cfg.py.in
-    ${LIBC_BUILD_DIR}/test/lit.site.cfg.py
-    MAIN_CONFIG
-    ${LIBC_SOURCE_DIR}/test/lit.cfg.py
-    PATHS
-    "LLVM_SOURCE_DIR"
-    "LLVM_BINARY_DIR"
-    "LLVM_TOOLS_DIR"
-    "LLVM_LIBS_DIR"
-    "LIBC_SOURCE_DIR"
-    "LIBC_BINARY_DIR"
-  )
-endfunction()
-
-#-------------------------------------------------------------------------------
-# add_libc_lit_testsuite()
-#
-# Creates a lit test suite target for a specific test directory.
-#
-# Usage:
-#   add_libc_lit_testsuite(check-libc-ctype
-#     SUITE_NAME ctype
-#     TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/ctype
-#     DEPENDS libc-ctype-tests
-#   )
-#
-# Note: TEST_DIR should be the build directory where test executables are
-# located, not the source directory.
-#-------------------------------------------------------------------------------
-function(add_libc_lit_testsuite target_name)
-  cmake_parse_arguments(
-    "LIT_SUITE"
-    ""                    # No optional arguments
-    "SUITE_NAME;TEST_DIR" # Single value arguments
-    "DEPENDS"             # Multi value arguments
-    ${ARGN}
-  )
-  
-  if(NOT LIT_SUITE_TEST_DIR)
-    message(FATAL_ERROR "add_libc_lit_testsuite requires TEST_DIR")
-  endif()
-  
-  # Create the lit test target using LLVM's infrastructure
-  add_lit_testsuite(${target_name}
-    "Running ${LIT_SUITE_SUITE_NAME} libc tests"
-    ${LIT_SUITE_TEST_DIR}
-    DEPENDS ${LIT_SUITE_DEPENDS}
-  )
-  
-  # Add to the umbrella check-libc-lit target if it exists
-  if(TARGET check-libc-lit)
-    add_dependencies(check-libc-lit ${target_name})
-  endif()
-endfunction()
diff --git a/libc/cmake/modules/LLVMLibCRules.cmake b/libc/cmake/modules/LLVMLibCRules.cmake
index 029ecc390128d..76f3892eeb32e 100644
--- a/libc/cmake/modules/LLVMLibCRules.cmake
+++ b/libc/cmake/modules/LLVMLibCRules.cmake
@@ -4,4 +4,3 @@ include(LLVMLibCFlagRules)
 include(LLVMLibCObjectRules)
 include(LLVMLibCLibraryRules)
 include(LLVMLibCTestRules)
-include(LLVMLibCLitTestRules)
diff --git a/libc/test/CMakeLists.txt b/libc/test/CMakeLists.txt
index 30e4c4239174e..88663367feb04 100644
--- a/libc/test/CMakeLists.txt
+++ b/libc/test/CMakeLists.txt
@@ -10,9 +10,21 @@ add_custom_target(libc-long-running-tests)
 add_custom_target(libc-unit-tests-build)
 add_custom_target(libc-hermetic-tests-build)
 
-# Configure lit test infrastructure
-# Lit depends on build-only targets so tests aren't run during build
-configure_libc_lit_site_cfg()
+# Configure the site config file for lit
+configure_lit_site_cfg(
+  ${LIBC_SOURCE_DIR}/test/lit.site.cfg.py.in
+  ${LIBC_BUILD_DIR}/test/lit.site.cfg.py
+  MAIN_CONFIG
+  ${LIBC_SOURCE_DIR}/test/lit.cfg.py
+  PATHS
+  "LLVM_SOURCE_DIR"
+  "LLVM_BINARY_DIR"
+  "LLVM_TOOLS_DIR"
+  "LLVM_LIBS_DIR"
+  "LIBC_SOURCE_DIR"
+  "LIBC_BUILD_DIR"
+)
+
 add_lit_testsuite(check-libc-lit
   "Running libc tests via lit"
   ${LIBC_BUILD_DIR}/test
diff --git a/libc/test/lit.cfg.py b/libc/test/lit.cfg.py
index 9cc9f6be446cf..9791a24f9a56e 100644
--- a/libc/test/lit.cfg.py
+++ b/libc/test/lit.cfg.py
@@ -4,46 +4,14 @@
 # See https://llvm.org/LICENSE.txt for license information.
 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 #
-# ===----------------------------------------------------------------------===##
+# All the Lit configuration is handled in the site config -- this file is only
+# left as a canary to catch invocations of Lit that do not go through llvm-lit.
 #
-# This is the lit configuration for LLVM libc tests.
-#
-# ===----------------------------------------------------------------------===##
-
-import os
-import site
-import sys
-
-import lit.formats
-import lit.util
-
-# Add libc's utils directory to the path so we can import the test format
-site.addsitedir(os.path.join(config.libc_src_root, "utils"))
-import LibcTestFormat
-
-# Configuration file for the 'lit' test runner.
-
-# name: The name of this test suite.
-config.name = "libc"
-
-# testFormat: Use libc's custom test format that discovers pre-built
-# test executables (Libc*Tests) in the build directory.
-config.test_format = LibcTestFormat.LibcTest()
-
-# suffixes: Not used by LibcTest format, but kept for compatibility
-config.suffixes = []
-
-# excludes: A list of directories to exclude from the testsuite.
-config.excludes = ["Inputs", "CMakeLists.txt", "README.txt", "LICENSE.txt", "UnitTest"]
-
-# test_source_root: The root path where tests are located.
-# test_exec_root: The root path where test executables are built.
-# Set both to the build directory so ExecutableTest finds executables correctly.
-config.test_exec_root = os.path.join(config.libc_obj_root, "test")
-config.test_source_root = config.test_exec_root
-
-# Add tool directories to PATH (in case we add FileCheck tests later)
-if hasattr(config, "llvm_tools_dir") and config.llvm_tools_dir:
-    config.environment["PATH"] = os.path.pathsep.join(
-        [config.llvm_tools_dir, config.environment.get("PATH", "")]
-    )
+# Invocations that go through llvm-lit will automatically use the right Lit
+# site configuration inside the build directory.
+
+lit_config.fatal(
+    "You seem to be running Lit directly -- you should be running Lit through "
+    "<build>/bin/llvm-lit, which will ensure that the right Lit configuration "
+    "file is used."
+)
diff --git a/libc/test/lit.site.cfg.py.in b/libc/test/lit.site.cfg.py.in
index b4fd7d07ba716..87a5649e4ba6a 100644
--- a/libc/test/lit.site.cfg.py.in
+++ b/libc/test/lit.site.cfg.py.in
@@ -1,23 +1,37 @@
 @LIT_SITE_CFG_IN_HEADER@
 
-import sys
 import os
+import site
 
 # Configuration values from CMake
-config.llvm_src_root = path(r"@LLVM_SOURCE_DIR@")
-config.llvm_obj_root = path(r"@LLVM_BINARY_DIR@")
 config.llvm_tools_dir = lit_config.substitute(path(r"@LLVM_TOOLS_DIR@"))
-config.llvm_libs_dir = lit_config.substitute(path(r"@LLVM_LIBS_DIR@"))
 config.libc_src_root = path(r"@LIBC_SOURCE_DIR@")
-config.libc_obj_root = path(r"@LIBC_BINARY_DIR@")
-config.target_triple = "@LLVM_TARGET_TRIPLE@"
-config.host_triple = "@LLVM_HOST_TRIPLE@"
-config.python_executable = "@Python3_EXECUTABLE@"
-
-# Initialize lit.llvm module
-import lit.llvm
-lit.llvm.initialize(lit_config, config)
-
-# Let the main config do the real work.
-lit_config.load_config(
-    config, os.path.join(config.libc_src_root, "test/lit.cfg.py"))
+config.libc_obj_root = path(r"@LIBC_BUILD_DIR@")
+config.libc_test_cmd = "@LIBC_TEST_CMD@"
+
+# Add libc's utils directory to the path so we can import the test format.
+site.addsitedir(os.path.join(config.libc_src_root, "utils"))
+import libctest
+
+# name: The name of this test suite.
+config.name = "libc"
+
+# testFormat: Use libc's custom test format that discovers pre-built
+# test executables (Libc*Tests) in the build directory.
+config.test_format = libctest.LibcTest()
+
+# excludes: A list of directories to exclude from the testsuite.
+config.excludes = ["Inputs", "CMakeLists.txt", "README.txt", "LICENSE.txt", "UnitTest"]
+
+# test_source_root: The root path where tests are located.
+# test_exec_root: The root path where test executables are built.
+# Set both to the build directory so ExecutableTest finds executables correctly.
+config.test_exec_root = os.path.join(config.libc_obj_root, "test")
+config.test_source_root = config.test_exec_root
+
+# Add tool directories to PATH (in case we add FileCheck tests later).
+if hasattr(config, "llvm_tools_dir") and config.llvm_tools_dir:
+    config.environment["PATH"] = os.path.pathsep.join(
+        [config.llvm_tools_dir, config.environment.get("PATH", "")]
+    )
+
diff --git a/libc/utils/LibcTestFormat/__init__.py b/libc/utils/libctest/__init__.py
similarity index 100%
rename from libc/utils/LibcTestFormat/__init__.py
rename to libc/utils/libctest/__init__.py
diff --git a/libc/utils/LibcTestFormat/format.py b/libc/utils/libctest/format.py
similarity index 81%
rename from libc/utils/LibcTestFormat/format.py
rename to libc/utils/libctest/format.py
index 8b641f169e243..df7d402799762 100644
--- a/libc/utils/LibcTestFormat/format.py
+++ b/libc/utils/libctest/format.py
@@ -23,6 +23,7 @@
 """
 
 import os
+import shlex
 
 import lit.formats
 import lit.Test
@@ -52,7 +53,8 @@ def getTestsInDirectory(self, testSuite, path_in_suite, litConfig, localConfig):
         if not os.path.isdir(source_path):
             return
 
-        for filename in os.listdir(source_path):
+        # Sort for deterministic test discovery/output ordering.
+        for filename in sorted(os.listdir(source_path)):
             filepath = os.path.join(source_path, filename)
 
             # Match our test executable pattern
@@ -85,12 +87,20 @@ def execute(self, test, litConfig):
         testdata/test.txt) work correctly.
         """
 
-        testPath = test.getSourcePath()
-        execDir = os.path.dirname(testPath)
+        test_path = test.getSourcePath()
+        exec_dir = os.path.dirname(test_path)
 
-        out, err, exitCode = lit.util.executeCommand(testPath, cwd=execDir)
+        test_cmd_template = getattr(test.config, "libc_test_cmd", "")
+        if test_cmd_template:
+            test_cmd = test_cmd_template.replace("@BINARY@", test_path)
+            cmd_args = shlex.split(test_cmd)
+            if not cmd_args:
+                cmd_args = [test_path]
+            out, err, exit_code = lit.util.executeCommand(cmd_args, cwd=exec_dir)
+        else:
+            out, err, exit_code = lit.util.executeCommand([test_path], cwd=exec_dir)
 
-        if not exitCode:
+        if not exit_code:
             return lit.Test.PASS, ""
 
         return lit.Test.FAIL, out + err



More information about the libc-commits mailing list