diff --git a/ffi/CMakeLists.txt b/ffi/CMakeLists.txt index a8c09f1885a3..0690c926e0f5 100644 --- a/ffi/CMakeLists.txt +++ b/ffi/CMakeLists.txt @@ -218,6 +218,20 @@ if (TVM_FFI_BUILD_PYTHON_MODULE) target_compile_features(tvm_ffi_cython PRIVATE cxx_std_17) target_link_libraries(tvm_ffi_cython PRIVATE tvm_ffi_header) target_link_libraries(tvm_ffi_cython PRIVATE tvm_ffi_shared) + # Set RPATH for tvm_ffi_cython to find tvm_ffi_shared.so relatively + if(APPLE) + # macOS uses @loader_path + set_target_properties(tvm_ffi_cython PROPERTIES + INSTALL_RPATH "@loader_path/lib" + BUILD_WITH_INSTALL_RPATH ON + ) + elseif(LINUX) + # Linux uses $ORIGIN + set_target_properties(tvm_ffi_cython PROPERTIES + INSTALL_RPATH "\$ORIGIN/lib" + BUILD_WITH_INSTALL_RPATH ON + ) + endif() install(TARGETS tvm_ffi_cython DESTINATION .) ########## Installing the source ########## diff --git a/ffi/pyproject.toml b/ffi/pyproject.toml index eac5a358b95f..60fdb27b5a43 100644 --- a/ffi/pyproject.toml +++ b/ffi/pyproject.toml @@ -17,7 +17,7 @@ [project] name = "apache-tvm-ffi" -version = "0.1.0a0" +version = "0.1.0a2" description = "tvm ffi" authors = [{ name = "TVM FFI team" }] @@ -32,6 +32,7 @@ classifiers = [ ] keywords = ["machine learning", "inference"] requires-python = ">=3.9" + dependencies = [] @@ -40,8 +41,9 @@ Homepage = "https://github.com/apache/tvm/ffi" GitHub = "https://github.com/apache/tvm/ffi" [project.optional-dependencies] -torch = ["torch"] -test = ["pytest"] +# setup tools is needed by torch jit for best perf +torch = ["torch", "setuptools"] +test = ["pytest", "numpy", "torch"] [project.scripts] tvm-ffi-config = "tvm_ffi.config:__main__" @@ -122,20 +124,27 @@ skip_gitignore = true [tool.cibuildwheel] build-verbosity = 1 -# skip pp and low python version -# sdist should be sufficient + +# only build up to cp312, cp312 +# will be abi3 and can be used in future versions +build = [ + "cp39-*", + "cp310-*", + "cp311-*", + "cp312-*", +] skip = [ - "cp36-*", - "cp37-*", - "cp38-*", + "*musllinux*" +] +# we only need to test on cp312 +test-skip = [ "cp39-*", "cp310-*", "cp311-*", - "pp*", - "*musllinux*", -] # pypy doesn't play nice with pybind11 +] +# focus on testing abi3 wheel build-frontend = "build[uv]" -test-command = "pytest {project}/tests -m " +test-command = "pytest {package}/tests/python -vvs" test-extras = ["test"] [tool.cibuildwheel.linux] diff --git a/ffi/python/tvm_ffi/cython/function.pxi b/ffi/python/tvm_ffi/cython/function.pxi index 2a2ee855f50a..dcd300c9b036 100644 --- a/ffi/python/tvm_ffi/cython/function.pxi +++ b/ffi/python/tvm_ffi/cython/function.pxi @@ -27,8 +27,6 @@ except ImportError: def load_torch_get_current_cuda_stream(): """Create a faster get_current_cuda_stream for torch through cpp extension. """ - from torch.utils import cpp_extension - source = """ #include @@ -44,6 +42,7 @@ def load_torch_get_current_cuda_stream(): """Fallback with python api""" return torch.cuda.current_stream(device_id).cuda_stream try: + from torch.utils import cpp_extension result = cpp_extension.load_inline( name="get_current_cuda_stream", cpp_sources=[source], @@ -56,6 +55,7 @@ def load_torch_get_current_cuda_stream(): except Exception: return fallback_get_current_cuda_stream + if torch is not None: # when torch is available, jit compile the get_current_cuda_stream function # the torch caches the extension so second loading is faster diff --git a/ffi/python/tvm_ffi/dtype.py b/ffi/python/tvm_ffi/dtype.py index 32986a4eb0bf..cd9561695503 100644 --- a/ffi/python/tvm_ffi/dtype.py +++ b/ffi/python/tvm_ffi/dtype.py @@ -17,7 +17,6 @@ """dtype class.""" # pylint: disable=invalid-name from enum import IntEnum -import numpy as np from . import core @@ -58,22 +57,7 @@ class dtype(str): __slots__ = ["__tvm_ffi_dtype__"] - NUMPY_DTYPE_TO_STR = { - np.dtype(np.bool_): "bool", - np.dtype(np.int8): "int8", - np.dtype(np.int16): "int16", - np.dtype(np.int32): "int32", - np.dtype(np.int64): "int64", - np.dtype(np.uint8): "uint8", - np.dtype(np.uint16): "uint16", - np.dtype(np.uint32): "uint32", - np.dtype(np.uint64): "uint64", - np.dtype(np.float16): "float16", - np.dtype(np.float32): "float32", - np.dtype(np.float64): "float64", - } - if hasattr(np, "float_"): - NUMPY_DTYPE_TO_STR[np.dtype(np.float_)] = "float64" + NUMPY_DTYPE_TO_STR = {} def __new__(cls, content): content = str(content) @@ -122,6 +106,28 @@ def lanes(self): return self.__tvm_ffi_dtype__.lanes +try: + # this helps to make numpy as optional + # although almost in all cases we want numpy + import numpy as np + + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.bool_)] = "bool" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.int8)] = "int8" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.int16)] = "int16" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.int32)] = "int32" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.int64)] = "int64" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.uint8)] = "uint8" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.uint16)] = "uint16" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.uint32)] = "uint32" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.uint64)] = "uint64" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.float16)] = "float16" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.float32)] = "float32" + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.float64)] = "float64" + if hasattr(np, "float_"): + dtype.NUMPY_DTYPE_TO_STR[np.dtype(np.float_)] = "float64" +except ImportError: + pass + try: import ml_dtypes diff --git a/ffi/scripts/run_tests.sh b/ffi/scripts/run_tests.sh index 7fe292a12ce2..118162569cb9 100755 --- a/ffi/scripts/run_tests.sh +++ b/ffi/scripts/run_tests.sh @@ -19,8 +19,7 @@ set -euxo pipefail BUILD_TYPE=Release -rm -rf build/CMakeFiles build/CMakeCache.txt -cmake -G Ninja -S . -B build -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_FLAGS="-O3" -cmake --build build --parallel 16 --clean-first --config ${BUILD_TYPE} --target tvm_ffi_tests +cmake -G Ninja -S . -B build -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache +cmake --build build --clean-first --config ${BUILD_TYPE} --target tvm_ffi_tests GTEST_COLOR=1 ctest -V -C ${BUILD_TYPE} --test-dir build --output-on-failure diff --git a/ffi/tests/python/test_error.py b/ffi/tests/python/test_error.py index 93019bb2a310..ad6da64c0f19 100644 --- a/ffi/tests/python/test_error.py +++ b/ffi/tests/python/test_error.py @@ -51,10 +51,6 @@ def test_error_from_cxx(): tvm_ffi.convert(lambda x: x)() -@pytest.mark.xfail( - "32bit" in platform.architecture() or platform.system() == "Windows", - reason="May fail if debug symbols are missing", -) def test_error_from_nested_pyfunc(): fapply = tvm_ffi.convert(lambda f, *args: f(*args)) cxx_test_raise_error = tvm_ffi.get_global_func("testing.test_raise_error") @@ -78,13 +74,17 @@ def raise_error(): traceback = e.__tvm_ffi_error__.traceback assert e.__tvm_ffi_error__.same_as(record_object[0]) assert traceback.count("TestRaiseError") == 1 - assert traceback.count("TestApply") == 1 - assert traceback.count("") == 1 - pos_cxx_raise = traceback.find("TestRaiseError") - pos_cxx_apply = traceback.find("TestApply") - pos_lambda = traceback.find("") - assert pos_cxx_raise > pos_lambda - assert pos_lambda > pos_cxx_apply + # The following lines may fail if debug symbols are missing + try: + assert traceback.count("TestApply") == 1 + assert traceback.count("") == 1 + pos_cxx_raise = traceback.find("TestRaiseError") + pos_cxx_apply = traceback.find("TestApply") + pos_lambda = traceback.find("") + assert pos_cxx_raise > pos_lambda + assert pos_lambda > pos_cxx_apply + except Exception as e: + pytest.xfail("May fail if debug symbols are missing") def test_error_traceback_update():