diff --git a/include/xgboost/c_api.h b/include/xgboost/c_api.h index e935bae47..8d929593c 100644 --- a/include/xgboost/c_api.h +++ b/include/xgboost/c_api.h @@ -42,6 +42,15 @@ typedef void *BoosterHandle; // NOLINT(*) */ XGB_DLL void XGBoostVersion(int* major, int* minor, int* patch); +/*! + * \brief Get compile information of shared library. + * + * \param out string encoded JSON object containing build flags and dependency version. + * + * \return 0 for success, -1 for failure + */ +XGB_DLL int XGBBuildInfo(char const **out); + /*! * \brief get string message of the last error * diff --git a/python-package/xgboost/__init__.py b/python-package/xgboost/__init__.py index 7c1078c13..aacd88dd2 100644 --- a/python-package/xgboost/__init__.py +++ b/python-package/xgboost/__init__.py @@ -6,7 +6,7 @@ Contributors: https://github.com/dmlc/xgboost/blob/master/CONTRIBUTORS.md import os -from .core import DMatrix, DeviceQuantileDMatrix, Booster, DataIter +from .core import DMatrix, DeviceQuantileDMatrix, Booster, DataIter, build_info from .training import train, cv from . import rabit # noqa from . import tracker # noqa @@ -21,14 +21,34 @@ try: except ImportError: pass -VERSION_FILE = os.path.join(os.path.dirname(__file__), 'VERSION') +VERSION_FILE = os.path.join(os.path.dirname(__file__), "VERSION") with open(VERSION_FILE, encoding="ascii") as f: __version__ = f.read().strip() -__all__ = ['DMatrix', 'DeviceQuantileDMatrix', 'Booster', 'DataIter', - 'train', 'cv', - 'RabitTracker', - 'XGBModel', 'XGBClassifier', 'XGBRegressor', 'XGBRanker', - 'XGBRFClassifier', 'XGBRFRegressor', - 'plot_importance', 'plot_tree', 'to_graphviz', 'dask', - 'set_config', 'get_config', 'config_context'] +__all__ = [ + # core + "DMatrix", + "DeviceQuantileDMatrix", + "Booster", + "DataIter", + "train", + "cv", + # utilities + "RabitTracker", + "build_info", + "plot_importance", + "plot_tree", + "to_graphviz", + "set_config", + "get_config", + "config_context", + # sklearn + "XGBModel", + "XGBClassifier", + "XGBRegressor", + "XGBRanker", + "XGBRFClassifier", + "XGBRFRegressor", + # dask + "dask", +] diff --git a/python-package/xgboost/core.py b/python-package/xgboost/core.py index 464e88656..079be9664 100644 --- a/python-package/xgboost/core.py +++ b/python-package/xgboost/core.py @@ -192,6 +192,22 @@ def _check_call(ret: int) -> None: raise XGBoostError(py_str(_LIB.XGBGetLastError())) +def build_info() -> dict: + """Build information of XGBoost. The returned value format is not stable. Also, please + note that build time dependency is not the same as runtime dependency. For instance, + it's possible to build XGBoost with older CUDA version but run it with the lastest + one. + + .. versionadded:: 1.6.0 + + """ + j_info = ctypes.c_char_p() + _check_call(_LIB.XGBBuildInfo(ctypes.byref(j_info))) + assert j_info.value is not None + res = json.loads(j_info.value.decode()) # pylint: disable=no-member + return res + + def _numpy2ctypes_type(dtype): _NUMPY_TO_CTYPES_MAPPING = { np.float32: ctypes.c_float, diff --git a/src/c_api/c_api.cc b/src/c_api/c_api.cc index 42b9cad6f..98a2759c8 100644 --- a/src/c_api/c_api.cc +++ b/src/c_api/c_api.cc @@ -42,6 +42,68 @@ XGB_DLL void XGBoostVersion(int* major, int* minor, int* patch) { } } +using GlobalConfigAPIThreadLocalStore = dmlc::ThreadLocalStore; + +#if !defined(XGBOOST_USE_CUDA) +namespace xgboost { +void XGBBuildInfoDevice(Json *p_info) { + auto &info = *p_info; + info["USE_CUDA"] = Boolean{false}; + info["USE_NCCL"] = Boolean{false}; + info["USE_RMM"] = Boolean{false}; +} +} // namespace xgboost +#endif + +XGB_DLL int XGBBuildInfo(char const **out) { + API_BEGIN(); + CHECK(out) << "Invalid input pointer"; + Json info{Object{}}; + +#if defined(XGBOOST_BUILTIN_PREFETCH_PRESENT) + info["BUILTIN_PREFETCH_PRESENT"] = Boolean{true}; +#else + info["BUILTIN_PREFETCH_PRESENT"] = Boolean{false}; +#endif + +#if defined(XGBOOST_MM_PREFETCH_PRESENT) + info["MM_PREFETCH_PRESENT"] = Boolean{true}; +#else + info["MM_PREFETCH_PRESENT"] = Boolean{false}; +#endif + +#if defined(_OPENMP) + info["USE_OPENMP"] = Boolean{true}; +#else + info["USE_OPENMP"] = Boolean{false}; +#endif + +#if defined(__GNUC__) && !defined(__clang__) + info["GCC_VERSION"] = std::vector{Json{Integer{__GNUC__}}, Json{Integer{__GNUC_MINOR__}}, + Json{Integer{__GNUC_PATCHLEVEL__}}}; +#endif + +#if defined(__clang__) + info["CLANG_VERSION"] = + std::vector{Json{Integer{__clang_major__}}, Json{Integer{__clang_minor__}}, + Json{Integer{__clang_patchlevel__}}}; +#endif + +#if !defined(NDEBUG) + info["DEBUG"] = Boolean{true}; +#else + info["DEBUG"] = Boolean{false}; +#endif + + XGBBuildInfoDevice(&info); + + auto &out_str = GlobalConfigAPIThreadLocalStore::Get()->ret_str; + Json::Dump(info, &out_str); + *out = out_str.c_str(); + + API_END(); +} + XGB_DLL int XGBRegisterLogCallback(void (*callback)(const char*)) { API_BEGIN_UNGUARD(); LogCallbackRegistry* registry = LogCallbackRegistryStore::Get(); @@ -95,8 +157,6 @@ XGB_DLL int XGBSetGlobalConfig(const char* json_str) { API_END(); } -using GlobalConfigAPIThreadLocalStore = dmlc::ThreadLocalStore; - XGB_DLL int XGBGetGlobalConfig(const char** json_str) { API_BEGIN(); auto const& global_config = *GlobalConfigThreadLocalStore::Get(); diff --git a/src/c_api/c_api.cu b/src/c_api/c_api.cu index 18a47f880..80408ba46 100644 --- a/src/c_api/c_api.cu +++ b/src/c_api/c_api.cu @@ -7,6 +7,37 @@ #include "../data/device_adapter.cuh" namespace xgboost { + +void XGBBuildInfoDevice(Json *p_info) { + auto &info = *p_info; + + info["USE_CUDA"] = true; + + std::vector v{Json{Integer{THRUST_MAJOR_VERSION}}, Json{Integer{THRUST_MINOR_VERSION}}, + Json{Integer{THRUST_SUBMINOR_VERSION}}}; + info["THRUST_VERSION"] = v; + + v = {Json{Integer{dh::CUDAVersion().first}}, Json{Integer{dh::CUDAVersion().second}}}; + info["CUDA_VERSION"] = v; + +#if defined(XGBOOST_USE_NCCL) + info["USE_NCCL"] = Boolean{true}; + v = {Json{Integer{NCCL_MAJOR}}, Json{Integer{NCCL_MINOR}}, Json{Integer{NCCL_PATCH}}}; + info["NCCL_VERSION"] = v; +#else + info["USE_NCCL"] = Boolean{false}; +#endif + +#if defined(XGBOOST_USE_RMM) + info["USE_RMM"] = Boolean{true}; + v = {Json{Integer{RMM_VERSION_MAJOR}}, Json{Integer{RMM_VERSION_MINOR}}, + Json{Integer{RMM_VERSION_PATCH}}}; + info["RMM_VERSION"] = v; +#else + info["USE_RMM"] = Boolean{false}; +#endif +} + void XGBoostAPIGuard::SetGPUAttribute() { try { device_id_ = dh::CurrentDevice(); diff --git a/src/c_api/c_api_utils.h b/src/c_api/c_api_utils.h index b044c6879..66825722b 100644 --- a/src/c_api/c_api_utils.h +++ b/src/c_api/c_api_utils.h @@ -239,5 +239,7 @@ inline void GenerateFeatureMap(Learner const *learner, } CHECK_EQ(feature_map.Size(), n_features); } + +void XGBBuildInfoDevice(Json* p_info); } // namespace xgboost #endif // XGBOOST_C_API_C_API_UTILS_H_ diff --git a/tests/cpp/c_api/test_c_api.cc b/tests/cpp/c_api/test_c_api.cc index edba3f8bd..d5f284fe0 100644 --- a/tests/cpp/c_api/test_c_api.cc +++ b/tests/cpp/c_api/test_c_api.cc @@ -278,4 +278,13 @@ TEST(CAPI, XGBGlobalConfig) { ASSERT_EQ(err.find("verbosity"), std::string::npos); } } + +TEST(CAPI, BuildInfo) { + char const* out; + XGBBuildInfo(&out); + auto loaded = Json::Load(StringView{out}); + ASSERT_TRUE(get(loaded).find("USE_OPENMP") != get(loaded).cend()); + ASSERT_TRUE(get(loaded).find("USE_CUDA") != get(loaded).cend()); + ASSERT_TRUE(get(loaded).find("USE_NCCL") != get(loaded).cend()); +} } // namespace xgboost