diff --git a/language-extensions/R/src/Logger.cpp b/language-extensions/R/src/Logger.cpp index 8a36d2b7..299a5f3d 100644 --- a/language-extensions/R/src/Logger.cpp +++ b/language-extensions/R/src/Logger.cpp @@ -27,7 +27,6 @@ //************************************************************************************************** #include -#include #include #include "Common.h" @@ -75,7 +74,7 @@ void Logger::Log(const string &msg) { #if defined(_DEBUG) || defined(_VERBOSE) string msgWithTimestamp = string(GetCurrentTimestamp()) + msg + "\n"; - cout << msgWithTimestamp; + fwrite(msgWithTimestamp.data(), 1, msgWithTimestamp.size(), stdout); #endif } @@ -135,10 +134,11 @@ const char* Logger::GetCurrentTimestamp() // Name: Logger::LogToStdErr // // Description: -// Logs the given message to stderr; if R is initialized uses its error printing function, -// else uses std::cerr. +// Writes the given message to stderr and flushes, matching the unit-buffered behaviour of the +// std::cerr insertion this replaced. // void Logger::LogToStdErr(const string &errorMsgWithTimestamp) { - cerr << errorMsgWithTimestamp; + fwrite(errorMsgWithTimestamp.data(), 1, errorMsgWithTimestamp.size(), stderr); + fflush(stderr); } diff --git a/language-extensions/R/src/RExtension.cpp b/language-extensions/R/src/RExtension.cpp index 36e02c87..d97c81d7 100644 --- a/language-extensions/R/src/RExtension.cpp +++ b/language-extensions/R/src/RExtension.cpp @@ -29,7 +29,6 @@ #include "Common.h" #include -#include #include #include diff --git a/language-extensions/R/src/RTypeUtils.cpp b/language-extensions/R/src/RTypeUtils.cpp index ceaba9e7..58b295ea 100644 --- a/language-extensions/R/src/RTypeUtils.cpp +++ b/language-extensions/R/src/RTypeUtils.cpp @@ -28,7 +28,6 @@ #include "Common.h" #include #include -#include #include "RTypeUtils.h" #include "Unicode.h" diff --git a/language-extensions/R/src/linux/IosBaseSentinel_linux.cpp b/language-extensions/R/src/linux/IosBaseSentinel_linux.cpp new file mode 100644 index 00000000..cd2c817e --- /dev/null +++ b/language-extensions/R/src/linux/IosBaseSentinel_linux.cpp @@ -0,0 +1,40 @@ +//************************************************************************************************** +// Copyright (C) Microsoft Corporation. +// +// @File: IosBaseSentinel_linux.cpp +// +// Purpose: +// Defines the initialisation sentinel locally so libRExtension does not import it from +// the host's libstdc++. Recent GCC releases version that symbol above what the oldest supported +// hosts provide, so importing it makes the library fail to load there. +// +// The other extensions drop the include instead. R cannot: Rcpp.h and RInside.h include +// , and every R translation unit reaches them through Common.h. +// +// The sentinel has no call site - the compiler emits the reference purely so the resulting +// version dependency refuses to load against a libstdc++ that would leave the standard streams +// unconstructed. Defining it here removes that dependency, so this file must also perform the +// initialisation the sentinel was guarding: that is the namespace-scope ios_base::Init below. +// A function-local static would never run, because the function is never called. +// +// extern "C" emits the mangled name verbatim; hidden visibility keeps it out of our exports. +// +//************************************************************************************************** + +#include + +extern "C" __attribute__((visibility("hidden"))) void _ZSt21ios_base_library_initv() +{ +} + +// Constructs std::cin/cout/cerr/clog before any other global constructor in this library can touch +// a stream. The constructor is refcounted and idempotent, and is old enough to be present in every +// libstdc++ we target, so it does not raise the ABI floor. +// +// init_priority is required, not cosmetic: dynamic initialisation order between translation units +// is unspecified, so at default priority an Rcpp / RInside global constructor could run first and +// touch a stream that is not yet constructed - exactly the case the sentinel we just defined away +// was guarding against. libstdc++ gives its own centralised initialiser priority 90 for this +// reason. Priorities 0-100 are reserved and warn, so use the lowest available, 101. +// +static std::ios_base::Init g_iosBaseInit __attribute__((init_priority(101))); diff --git a/language-extensions/java/include/JniTypeHelper.inl b/language-extensions/java/include/JniTypeHelper.inl index 0d6aec68..48c2fb01 100644 --- a/language-extensions/java/include/JniTypeHelper.inl +++ b/language-extensions/java/include/JniTypeHelper.inl @@ -10,7 +10,6 @@ //********************************************************************* #include #include -#include #include #include // Needed for memcpy in Linux diff --git a/language-extensions/java/src/Logger.cpp b/language-extensions/java/src/Logger.cpp index 8d16694e..0daebc0f 100644 --- a/language-extensions/java/src/Logger.cpp +++ b/language-extensions/java/src/Logger.cpp @@ -9,7 +9,7 @@ // //********************************************************************* #include "Logger.h" -#include +#include #include using namespace std; @@ -22,7 +22,11 @@ using namespace std; // void Logger::LogError(const string &errorMsg) { - cerr << GetCurrentTimestamp() << "Error: " << errorMsg << endl; + fputs(GetCurrentTimestamp(), stderr); + fputs("Error: ", stderr); + fwrite(errorMsg.data(), 1, errorMsg.size(), stderr); + fputc('\n', stderr); + fflush(stderr); } //--------------------------------------------------------------------- @@ -34,7 +38,11 @@ void Logger::LogError(const string &errorMsg) // void Logger::LogException(const exception &e) { - cerr << GetCurrentTimestamp() << "Exception occurred: " << e.what() << endl; + fputs(GetCurrentTimestamp(), stderr); + fputs("Exception occurred: ", stderr); + fputs(e.what(), stderr); + fputc('\n', stderr); + fflush(stderr); } //--------------------------------------------------------------------- @@ -46,7 +54,11 @@ void Logger::LogException(const exception &e) // void Logger::LogJavaException(const string &exceptionMsg) { - cerr << GetCurrentTimestamp() << "Exception occurred in Java: " << exceptionMsg << endl; + fputs(GetCurrentTimestamp(), stderr); + fputs("Exception occurred in Java: ", stderr); + fwrite(exceptionMsg.data(), 1, exceptionMsg.size(), stderr); + fputc('\n', stderr); + fflush(stderr); } //--------------------------------------------------------------------- @@ -58,6 +70,9 @@ void Logger::LogJavaException(const string &exceptionMsg) void Logger::Log(const string &msg) { #ifdef DEBUG - cout << GetCurrentTimestamp() << msg << endl; + fputs(GetCurrentTimestamp(), stdout); + fwrite(msg.data(), 1, msg.size(), stdout); + fputc('\n', stdout); + fflush(stdout); #endif } diff --git a/language-extensions/python/include/Common.h b/language-extensions/python/include/Common.h index aa8da254..91db4fb1 100644 --- a/language-extensions/python/include/Common.h +++ b/language-extensions/python/include/Common.h @@ -18,7 +18,6 @@ #endif #include -#include #include #include #include diff --git a/language-extensions/python/src/CMakeLists.txt b/language-extensions/python/src/CMakeLists.txt index fd9112c4..81a94390 100644 --- a/language-extensions/python/src/CMakeLists.txt +++ b/language-extensions/python/src/CMakeLists.txt @@ -72,8 +72,30 @@ if (${PLATFORM} STREQUAL linux) message(STATUS "Shared Python library not found in standard locations, trying system default search") find_library(PYTHON_LIB python${PYTHON_VERSION}) endif() - find_library(BOOST_PYTHON_LIB boost_python${PYTHON_VERSION_NO_DOT} ${BOOST_PYTHON_ROOT}) - find_library(BOOST_NUMPY_LIB boost_numpy${PYTHON_VERSION_NO_DOT} ${BOOST_PYTHON_ROOT}) + + # Boost must not become a runtime dependency: the SQL container images ship no libboost_*.so, + # so a shared link fails at load time and every external script fails with it. + # + if(NOT BOOST_PYTHON_ROOT) + set(BOOST_PYTHON_ROOT /usr/local/lib/boost_1_90_0/stage/lib) + endif() + + unset(BOOST_PYTHON_LIB CACHE) + unset(BOOST_NUMPY_LIB CACHE) + + find_library(BOOST_PYTHON_LIB NAMES libboost_python${PYTHON_VERSION_NO_DOT}.a + PATHS ${BOOST_PYTHON_ROOT} NO_DEFAULT_PATH) + find_library(BOOST_NUMPY_LIB NAMES libboost_numpy${PYTHON_VERSION_NO_DOT}.a + PATHS ${BOOST_PYTHON_ROOT} NO_DEFAULT_PATH) + + # Fail at configure time rather than silently shipping a package that cannot load. + # + if(NOT BOOST_PYTHON_LIB OR NOT BOOST_NUMPY_LIB) + message(FATAL_ERROR + "libboost_python${PYTHON_VERSION_NO_DOT}.a / libboost_numpy${PYTHON_VERSION_NO_DOT}.a " + "not found under ${BOOST_PYTHON_ROOT}. Override -DBOOST_PYTHON_ROOT= if the " + "build moved them.") + endif() file(TO_CMAKE_PATH ${INCLUDE_ROOT}/python${PYTHON_VERSION} PYTHON_INCLUDE) file(TO_CMAKE_PATH ${INCLUDE_ROOT}/boost BOOST_INCLUDE) diff --git a/language-extensions/python/src/Logger.cpp b/language-extensions/python/src/Logger.cpp index 324fc7cc..929acc22 100644 --- a/language-extensions/python/src/Logger.cpp +++ b/language-extensions/python/src/Logger.cpp @@ -11,6 +11,8 @@ // //************************************************************************************************* +#include + #include "Logger.h" #define TIMESTAMP_LENGTH 35 @@ -26,7 +28,12 @@ char Logger::sm_timestampBuffer[TIMESTAMP_LENGTH] = { 0 }; // void Logger::LogError(const string &errorMsg) { - cerr << GetCurrentTimestamp() << "Error: " << errorMsg << endl; + const string timestamp = GetCurrentTimestamp(); + fwrite(timestamp.data(), 1, timestamp.size(), stderr); + fputs("Error: ", stderr); + fwrite(errorMsg.data(), 1, errorMsg.size(), stderr); + fputc('\n', stderr); + fflush(stderr); } //------------------------------------------------------------------------------------------------- @@ -38,7 +45,12 @@ void Logger::LogError(const string &errorMsg) // void Logger::LogException(const exception &e) { - cerr << GetCurrentTimestamp() << "Exception occurred: " << e.what() << endl; + const string timestamp = GetCurrentTimestamp(); + fwrite(timestamp.data(), 1, timestamp.size(), stderr); + fputs("Exception occurred: ", stderr); + fputs(e.what(), stderr); + fputc('\n', stderr); + fflush(stderr); } //------------------------------------------------------------------------------------------------- @@ -50,7 +62,11 @@ void Logger::LogException(const exception &e) void Logger::Log(const string &msg) { #if defined(_DEBUG) - cout << GetCurrentTimestamp() << msg << endl; + const string timestamp = GetCurrentTimestamp(); + fwrite(timestamp.data(), 1, timestamp.size(), stdout); + fwrite(msg.data(), 1, msg.size(), stdout); + fputc('\n', stdout); + fflush(stdout); #endif } diff --git a/language-extensions/python/src/PythonExtension.cpp b/language-extensions/python/src/PythonExtension.cpp index 7e9d6629..1883d928 100644 --- a/language-extensions/python/src/PythonExtension.cpp +++ b/language-extensions/python/src/PythonExtension.cpp @@ -14,7 +14,6 @@ //************************************************************************************************* #include -#include #include #include diff --git a/language-extensions/python/src/PythonLibrarySession.cpp b/language-extensions/python/src/PythonLibrarySession.cpp index 2d8f0275..f6cae65c 100644 --- a/language-extensions/python/src/PythonLibrarySession.cpp +++ b/language-extensions/python/src/PythonLibrarySession.cpp @@ -12,7 +12,6 @@ //************************************************************************************************* #include -#include #include #include diff --git a/language-extensions/python/src/PythonSession.cpp b/language-extensions/python/src/PythonSession.cpp index 6cf9637f..da476939 100644 --- a/language-extensions/python/src/PythonSession.cpp +++ b/language-extensions/python/src/PythonSession.cpp @@ -11,6 +11,8 @@ // //************************************************************************************************* +#include + #include "Logger.h" #include "PythonExtensionUtils.h" #include "PythonNamespace.h" @@ -211,8 +213,12 @@ void PythonSession::ExecuteWorkflow( string pyStdOut = bp::extract(m_mainNamespace["_temp_out_"]); string pyStdErr = bp::extract(m_mainNamespace["_temp_err_"]); - cout << pyStdOut << endl; - cerr << pyStdErr << endl; + fwrite(pyStdOut.data(), 1, pyStdOut.size(), stdout); + fputc('\n', stdout); + fflush(stdout); + fwrite(pyStdErr.data(), 1, pyStdErr.size(), stderr); + fputc('\n', stderr); + fflush(stderr); // In case of streaming clean up the previous stream batch's output buffers // diff --git a/language-extensions/python/test/src/CMakeLists.txt b/language-extensions/python/test/src/CMakeLists.txt index 317a9049..8339a6a7 100644 --- a/language-extensions/python/test/src/CMakeLists.txt +++ b/language-extensions/python/test/src/CMakeLists.txt @@ -46,8 +46,28 @@ if (${PLATFORM} STREQUAL linux) set(PYTHON_LIB_PATH ${PYTHONHOME}/lib/python${PYTHON_VERSION}/config-${PYTHON_VERSION}-x86_64-linux-gnu) find_library(PYTHON_LIB python${PYTHON_VERSION} ${PYTHON_LIB_PATH}) - find_library(BOOST_PYTHON_LIB boost_python${PYTHON_VERSION_NO_DOT} ${BOOST_PYTHON_ROOT}) - find_library(BOOST_NUMPY_LIB boost_numpy${PYTHON_VERSION_NO_DOT} ${BOOST_PYTHON_ROOT}) + + # Same static archives the extension links - see language-extensions/python/src/CMakeLists.txt. + # The test links libPythonExtension and Boost both, so a shared Boost here would put two + # Boost.Python copies with independent type registries in one process. + # + if(NOT BOOST_PYTHON_ROOT) + set(BOOST_PYTHON_ROOT /usr/local/lib/boost_1_90_0/stage/lib) + endif() + + unset(BOOST_PYTHON_LIB CACHE) + unset(BOOST_NUMPY_LIB CACHE) + + find_library(BOOST_PYTHON_LIB NAMES libboost_python${PYTHON_VERSION_NO_DOT}.a + PATHS ${BOOST_PYTHON_ROOT} NO_DEFAULT_PATH) + find_library(BOOST_NUMPY_LIB NAMES libboost_numpy${PYTHON_VERSION_NO_DOT}.a + PATHS ${BOOST_PYTHON_ROOT} NO_DEFAULT_PATH) + + if(NOT BOOST_PYTHON_LIB OR NOT BOOST_NUMPY_LIB) + message(FATAL_ERROR + "Static Boost.Python/Boost.NumPy not found under ${BOOST_PYTHON_ROOT}. " + "Override -DBOOST_PYTHON_ROOT= if the build moved them.") + endif() file(TO_CMAKE_PATH ${INCLUDE_ROOT}/python${PYTHON_VERSION} PYTHON_INCLUDE) file(TO_CMAKE_PATH ${INCLUDE_ROOT}/boost BOOST_INCLUDE)