[R-package] Use Rprintf for logging in the R package (fixes #1440, fixes #1909) (#2901)

* [R-package] started cutting over from custom R-to-C interface to R.h

* replaced LGBM_SE with SEXP

* fixed error about ocnflicting definitions of length

* got linking working

* more stuff

* eliminated R CMD CHECK note about printing

* switched from hard-coded include dir to the one from FindLibR.cmake

* cleaned up formatting in FindLibR.cmake

* commented-out everything in CI that does not touch R

* more changes

* trying to get better logs

* tried ignoring

* added error message to confirm a suspicion

* still trying to find R during R CMD CHECK

* restore full CI

* fixed comment

* Update R-package/src/cmake/modules/FindLibR.cmake

* changed strategy for finding LIBR_HOME on Windows

* Removed 32-bit Windows stuff in FindLibR.cmake

* Update R-package/src/cmake/modules/FindLibR.cmake

* Update CMakeLists.txt

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* Update CMakeLists.txt

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* Update R-package/src/cmake/modules/FindLibR.cmake

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* removed some duplication in cmake scripts

* Update R-package/src/cmake/modules/FindLibR.cmake

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* Update R-package/src/cmake/modules/FindLibR.cmake

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* Update R-package/src/cmake/modules/FindLibR.cmake

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* Update R-package/src/cmake/modules/FindLibR.cmake

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* Update R-package/src/cmake/modules/FindLibR.cmake

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* Update R-package/src/cmake/modules/FindLibR.cmake

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* added LIBR_CORE_LIBRARY back

* small fixes to CMakeLists

* simplified FindLibR.cmake

* some fixes for windows

* Apply suggestions from code review

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* allowed for directly passing LIBR_EXECUTABLE to FindLibR.cmake

* reorganized FindLibR.cmake to catch more cases

* clean up inconsistencies  in R calls in FindLibR.cmake

* Update R-package/src/cmake/modules/FindLibR.cmake

Co-Authored-By: Nikita Titov <nekit94-08@mail.ru>

* removed unnecessary log messages

* removed unnecessary unset() call

Co-authored-by: Nikita Titov <nekit94-08@mail.ru>
This commit is contained in:
James Lamb 2020-03-23 19:11:30 -05:00 коммит произвёл GitHub
Родитель a8c1e0a11a
Коммит 0341906c45
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 264 добавлений и 15 удалений

Просмотреть файл

@ -72,6 +72,12 @@ if(USE_R35)
endif(USE_R35)
if(BUILD_FOR_R)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules")
find_package(LibR REQUIRED)
message(STATUS "LIBR_EXECUTABLE: ${LIBR_EXECUTABLE}")
message(STATUS "LIBR_INCLUDE_DIRS: ${LIBR_INCLUDE_DIRS}")
message(STATUS "LIBR_CORE_LIBRARY: ${LIBR_CORE_LIBRARY}")
include_directories(${LIBR_INCLUDE_DIRS})
ADD_DEFINITIONS(-DLGB_R_BUILD)
endif(BUILD_FOR_R)
@ -301,6 +307,11 @@ if(WIN32 AND (MINGW OR CYGWIN))
TARGET_LINK_LIBRARIES(_lightgbm IPHLPAPI)
endif()
if(BUILD_FOR_R)
TARGET_LINK_LIBRARIES(lightgbm ${LIBR_CORE_LIBRARY})
TARGET_LINK_LIBRARIES(_lightgbm ${LIBR_CORE_LIBRARY})
endif(BUILD_FOR_R)
install(TARGETS lightgbm _lightgbm
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib

Просмотреть файл

@ -0,0 +1,208 @@
# CMake module used to find the location of R's
# dll and header files.
#
# Borrows heavily from xgboost's R package:
#
# * https://github.com/dmlc/xgboost/blob/master/cmake/modules/FindLibR.cmake
#
# Defines the following:
# LIBR_FOUND
# LIBR_HOME
# LIBR_EXECUTABLE
# LIBR_INCLUDE_DIRS
# LIBR_LIB_DIR
# LIBR_CORE_LIBRARY
# and a CMake function to create R.lib for MSVC
if(NOT R_ARCH)
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
set(R_ARCH "i386")
else()
set(R_ARCH "x64")
endif()
endif()
if(NOT ("${R_ARCH}" STREQUAL "x64"))
message(FATAL_ERROR "LightGBM's R package currently only supports 64-bit operating systems")
endif()
# Creates R.lib and R.def in the build directory for linking with MSVC
function(create_rlib_for_msvc)
message("Creating R.lib and R.def")
# various checks and warnings
if(NOT WIN32 OR NOT MSVC)
message(FATAL_ERROR "create_rlib_for_msvc() can only be used with MSVC")
endif()
if(NOT EXISTS "${LIBR_LIB_DIR}")
message(FATAL_ERROR "LIBR_LIB_DIR, '${LIBR_LIB_DIR}', not found")
endif()
find_program(GENDEF_EXE gendef)
find_program(DLLTOOL_EXE dlltool)
if(NOT GENDEF_EXE OR NOT DLLTOOL_EXE)
message(FATAL_ERROR "Either gendef.exe or dlltool.exe not found!\
\nDo you have Rtools installed with its MinGW's bin/ in PATH?")
endif()
# extract symbols from R.dll into R.def and R.lib import library
execute_process(COMMAND ${GENDEF_EXE}
"-" "${LIBR_LIB_DIR}/R.dll"
OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/R.def"
)
execute_process(COMMAND ${DLLTOOL_EXE}
"--input-def" "${CMAKE_CURRENT_BINARY_DIR}/R.def"
"--output-lib" "${CMAKE_CURRENT_BINARY_DIR}/R.lib"
)
endfunction(create_rlib_for_msvc)
# R version information is used to search for R's libraries in
# the registry on Windows. Since this code is orchestrated by
# an R script (src/install.libs.R), that script uses R's built-ins to
# find the version of R and pass it through as a CMake variable
if(CMAKE_R_VERSION)
message("R version passed into FindLibR.cmake: ${CMAKE_R_VERSION}")
elseif(WIN32)
message(FATAL_ERROR "Expected CMAKE_R_VERSION to be passed in on Windows but none was provided. Check src/install.libs.R")
endif()
if (NOT LIBR_EXECUTABLE)
find_program(
LIBR_EXECUTABLE
NAMES R R.exe
)
# CRAN may run RD CMD CHECK instead of R CMD CHECK,
# which can lead to this infamous error:
# 'R' should not be used without a path -- see par. 1.6 of the manual
if(LIBR_EXECUTABLE MATCHES ".*\\.Rcheck.*")
unset(LIBR_EXECUTABLE CACHE)
endif()
# ignore the R bundled with R.app on Mac, since that is GUI-only
if(LIBR_EXECUTABLE MATCHES ".+R\\.app.*")
unset(LIBR_EXECUTABLE CACHE)
endif()
endif()
# Find R executable unless it has been provided directly or already found
if (NOT LIBR_EXECUTABLE)
if(APPLE)
find_library(LIBR_LIBRARIES R)
if(LIBR_LIBRARIES MATCHES ".*\\.framework")
set(LIBR_HOME "${LIBR_LIBRARIES}/Resources")
set(LIBR_EXECUTABLE "${LIBR_HOME}/R")
else()
get_filename_component(_LIBR_LIBRARIES "${LIBR_LIBRARIES}" REALPATH)
get_filename_component(_LIBR_LIBRARIES_DIR "${_LIBR_LIBRARIES}" DIRECTORY)
set(LIBR_EXECUTABLE "${_LIBR_LIBRARIES_DIR}/../bin/R")
endif()
elseif(UNIX)
# attempt to find R executable
if(NOT LIBR_EXECUTABLE)
find_program(
LIBR_EXECUTABLE
NO_DEFAULT_PATH
HINTS "${CMAKE_CURRENT_BINARY_DIR}" "/usr/bin" "/usr/lib/" "/usr/local/bin/"
NAMES R
)
endif()
# Windows
else()
# if R executable not available, query R_HOME path from registry
if(NOT LIBR_HOME)
# Try to find R's location in the registry
# ref: https://cran.r-project.org/bin/windows/base/rw-FAQ.html#Does-R-use-the-Registry_003f
get_filename_component(
LIBR_HOME
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\R-core\\R\\${CMAKE_R_VERSION};InstallPath]"
ABSOLUTE
)
endif()
if(NOT LIBR_HOME)
get_filename_component(
LIBR_HOME
"[HKEY_CURRENT_USER\\SOFTWARE\\R-core\\R\\${CMAKE_R_VERSION};InstallPath]"
ABSOLUTE
)
endif()
if(NOT LIBR_HOME)
message(FATAL_ERROR "Unable to locate R executable.\
\nEither add its location to PATH or provide it through the LIBR_EXECUTABLE CMake variable")
endif()
# set exe location based on R_ARCH
set(LIBR_EXECUTABLE "${LIBR_HOME}/bin/${R_ARCH}/R.exe")
endif()
if(NOT LIBR_EXECUTABLE)
message(FATAL_ERROR "Unable to locate R executable.\
\nEither add its location to PATH or provide it through the LIBR_EXECUTABLE CMake variable")
endif()
endif()
# ask R for the home path
execute_process(
COMMAND ${LIBR_EXECUTABLE} "--slave" "--vanilla" "-e" "cat(normalizePath(R.home(), winslash='/'))"
OUTPUT_VARIABLE LIBR_HOME
)
# ask R for the include dir
execute_process(
COMMAND ${LIBR_EXECUTABLE} "--slave" "--vanilla" "-e" "cat(normalizePath(R.home('include'), winslash='/'))"
OUTPUT_VARIABLE LIBR_INCLUDE_DIRS
)
# ask R for the lib dir
execute_process(
COMMAND ${LIBR_EXECUTABLE} "--slave" "--vanilla" "-e" "cat(normalizePath(R.home('lib'), winslash='/'))"
OUTPUT_VARIABLE LIBR_LIB_DIR
)
# look for the core R library
find_library(
LIBR_CORE_LIBRARY
NAMES R
HINTS "${CMAKE_CURRENT_BINARY_DIR}" "${LIBR_LIB_DIR}" "${LIBR_HOME}/bin" "${LIBR_LIBRARIES}"
)
set(LIBR_HOME ${LIBR_HOME} CACHE PATH "R home directory")
set(LIBR_EXECUTABLE ${LIBR_EXECUTABLE} CACHE PATH "R executable")
set(LIBR_INCLUDE_DIRS ${LIBR_INCLUDE_DIRS} CACHE PATH "R include directory")
set(LIBR_LIB_DIR ${LIBR_LIB_DIR} CACHE PATH "R shared libraries directory")
set(LIBR_CORE_LIBRARY ${LIBR_CORE_LIBRARY} CACHE PATH "R core shared library")
if(WIN32 AND MSVC)
# create a local R.lib import library for R.dll if it doesn't exist
if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/R.lib")
create_rlib_for_msvc()
endif()
endif()
# define find requirements
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LibR DEFAULT_MSG
LIBR_HOME
LIBR_EXECUTABLE
LIBR_INCLUDE_DIRS
LIBR_LIB_DIR
LIBR_CORE_LIBRARY
)

Просмотреть файл

@ -53,6 +53,17 @@ if (!use_precompile) {
}
cmake_cmd <- paste0(cmake_cmd, " -DBUILD_FOR_R=ON ")
# Pass in R version, used to help find R executable for linking
R_version_string <- paste(
R.Version()[["major"]]
, R.Version()[["minor"]]
, sep = "."
)
cmake_cmd <- sprintf(
paste0(cmake_cmd, " -DCMAKE_R_VERSION='%s' ")
, R_version_string
)
# Check if Windows installation (for gcc vs Visual Studio)
if (WINDOWS) {
if (use_mingw) {

Просмотреть файл

@ -39,9 +39,9 @@
// 64bit pointer
#if INTPTR_MAX == INT64_MAX
typedef int64_t R_xlen_t;
typedef int64_t xlen_t;
#else
typedef int R_xlen_t;
typedef int xlen_t;
#endif
#else
@ -58,7 +58,7 @@
unsigned int gccls : 3;
};
typedef int R_xlen_t;
typedef int xlen_t;
#endif // R_VER_ABOVE_35
struct lgbm_primsxp {
@ -110,8 +110,8 @@ typedef struct LGBM_SER {
} LGBM_SER, *LGBM_SE;
struct lgbm_vecsxp {
R_xlen_t length;
R_xlen_t truelength;
xlen_t length;
xlen_t truelength;
};
typedef struct VECTOR_SER {

Просмотреть файл

@ -14,6 +14,13 @@
#include <iostream>
#include <stdexcept>
#ifdef LGB_R_BUILD
#define R_NO_REMAP
#define R_USE_C99_IN_CXX
#include <R_ext/Error.h>
#include <R_ext/Print.h>
#endif
namespace LightGBM {
#if defined(_MSC_VER)
@ -57,7 +64,6 @@ namespace LightGBM {
if ((pointer) == nullptr) LightGBM::Log::Fatal(#pointer " Can't be NULL at %s, line %d .\n", __FILE__, __LINE__);
#endif
enum class LogLevel: int {
Fatal = -1,
Warning = 0,
@ -65,7 +71,6 @@ enum class LogLevel: int {
Debug = 2,
};
/*!
* \brief A static Log class
*/
@ -107,19 +112,33 @@ class Log {
vsprintf(str_buf, format, val);
#endif
va_end(val);
fprintf(stderr, "[LightGBM] [Fatal] %s\n", str_buf);
fflush(stderr);
throw std::runtime_error(std::string(str_buf));
// R code should write back to R's error stream,
// otherwise to stderr
#ifndef LGB_R_BUILD
fprintf(stderr, "[LightGBM] [Fatal] %s\n", str_buf);
fflush(stderr);
throw std::runtime_error(std::string(str_buf));
#else
Rf_error("[LightGBM] [Fatal] %s\n", str_buf);
#endif
}
private:
static void Write(LogLevel level, const char* level_str, const char *format, va_list val) {
if (level <= GetLevel()) { // omit the message with low level
// write to STDOUT
printf("[LightGBM] [%s] ", level_str);
vprintf(format, val);
printf("\n");
fflush(stdout);
// R code should write back to R's output stream,
// otherwise to stdout
#ifndef LGB_R_BUILD
printf("[LightGBM] [%s] ", level_str);
vprintf(format, val);
printf("\n");
fflush(stdout);
#else
Rprintf("[LightGBM] [%s] ", level_str);
Rvprintf(format, val);
Rprintf("\n");
#endif
}
}