diff --git a/flang/runtime/CMakeLists.txt b/flang/runtime/CMakeLists.txt index ddd77f71fdbe..066cb7d12459 100644 --- a/flang/runtime/CMakeLists.txt +++ b/flang/runtime/CMakeLists.txt @@ -1,38 +1,62 @@ #===-- runtime/CMakeLists.txt ----------------------------------------------===# # # 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 # #===------------------------------------------------------------------------===# +include(CheckCXXSymbolExists) +include(CheckCXXSourceCompiles) +check_cxx_symbol_exists(strerror string.h HAVE_STRERROR) +check_cxx_symbol_exists(strerror_r string.h HAVE_STRERROR_R) +# Can't use symbol exists here as the function is overloaded in C++ +check_cxx_source_compiles( + "#include + int main() { + char buf[4096]; + return strerror_s(buf, 4096, 0); + } + " + HAVE_DECL_STRERROR_S) + +if (NOT (HAVE_STRERROR OR HAVE_STRERROR_R OR HAVE_DECL_STRERROR_S)) + message(FATAL_ERROR "None of strerror, strerror_r, strerror_s found.") +endif() + +configure_file(config.h.cmake config.h) + add_library(FortranRuntime ISO_Fortran_binding.cpp buffer.cpp connection.cpp derived-type.cpp descriptor.cpp edit-input.cpp edit-output.cpp environment.cpp file.cpp format.cpp internal-unit.cpp iostat.cpp io-api.cpp io-error.cpp io-stmt.cpp main.cpp memory.cpp stop.cpp terminator.cpp tools.cpp transformational.cpp type-code.cpp unit.cpp unit-map.cpp ) +target_include_directories(FortranRuntime + PRIVATE ${CMAKE_CURRENT_BINARY_DIR} +) + target_link_libraries(FortranRuntime FortranDecimal ) diff --git a/flang/runtime/config.h.cmake b/flang/runtime/config.h.cmake new file mode 100644 index 000000000000..0a1d1394b9bc --- /dev/null +++ b/flang/runtime/config.h.cmake @@ -0,0 +1,11 @@ +#ifndef FORTRAN_RUNTIME_CONFIG_H +#define FORTRAN_RUNTIME_CONFIG_H + +/* Define to 1 if you have the `strerror_r' function. */ +#cmakedefine01 HAVE_STRERROR_R + +/* Define to 1 if you have the declaration of `strerror_s', and to 0 if you + don't. */ +#cmakedefine01 HAVE_DECL_STRERROR_S + +#endif diff --git a/flang/runtime/io-error.cpp b/flang/runtime/io-error.cpp index 9300aa701f68..721ecf31fecc 100644 --- a/flang/runtime/io-error.cpp +++ b/flang/runtime/io-error.cpp @@ -1,81 +1,108 @@ //===-- runtime/io-error.cpp ------------------------------------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// +#include "config.h" #include "io-error.h" #include "magic-numbers.h" #include "tools.h" #include #include #include #include namespace Fortran::runtime::io { void IoErrorHandler::Begin(const char *sourceFileName, int sourceLine) { flags_ = 0; ioStat_ = 0; ioMsg_.reset(); SetLocation(sourceFileName, sourceLine); } void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) { if (iostatOrErrno == IostatEnd && (flags_ & hasEnd)) { if (!ioStat_ || ioStat_ < IostatEnd) { ioStat_ = IostatEnd; } } else if (iostatOrErrno == IostatEor && (flags_ & hasEor)) { if (!ioStat_ || ioStat_ < IostatEor) { ioStat_ = IostatEor; // least priority } } else if (iostatOrErrno != IostatOk) { if (flags_ & (hasIoStat | hasErr)) { if (ioStat_ <= 0) { ioStat_ = iostatOrErrno; // priority over END=/EOR= if (msg && (flags_ & hasIoMsg)) { char buffer[256]; va_list ap; va_start(ap, msg); std::vsnprintf(buffer, sizeof buffer, msg, ap); ioMsg_ = SaveDefaultCharacter(buffer, std::strlen(buffer) + 1, *this); } } } else if (msg) { va_list ap; va_start(ap, msg); CrashArgs(msg, ap); } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) { Crash(errstr); } else { Crash("I/O error (errno=%d): %s", iostatOrErrno, std::strerror(iostatOrErrno)); } } } void IoErrorHandler::SignalError(int iostatOrErrno) { SignalError(iostatOrErrno, nullptr); } void IoErrorHandler::SignalErrno() { SignalError(errno); } void IoErrorHandler::SignalEnd() { SignalError(IostatEnd); } void IoErrorHandler::SignalEor() { SignalError(IostatEor); } bool IoErrorHandler::GetIoMsg(char *buffer, std::size_t bufferLength) { const char *msg{ioMsg_.get()}; if (!msg) { msg = IostatErrorString(ioStat_); } if (msg) { ToFortranDefaultCharacter(buffer, bufferLength, msg); return true; } + + char *newBuf; + // Following code is taken from llvm/lib/Support/Errno.cpp + // in LLVM v9.0.1 +#if HAVE_STRERROR_R + // strerror_r is thread-safe. +#if defined(__GLIBC__) && defined(_GNU_SOURCE) + // glibc defines its own incompatible version of strerror_r + // which may not use the buffer supplied. + newBuf = ::strerror_r(ioStat_, buffer, bufferLength); +#else return ::strerror_r(ioStat_, buffer, bufferLength) == 0; +#endif +#elif HAVE_DECL_STRERROR_S // "Windows Secure API" + return ::strerror_s(buffer, bufferLength, ioStat_) == 0; +#elif HAVE_STRERROR + // Copy the thread un-safe result of strerror into + // the buffer as fast as possible to minimize impact + // of collision of strerror in multiple threads. + newBuf = strerror(ioStat_); +#else + // Strange that this system doesn't even have strerror + return false; +#endif + ::strncpy(buffer, newBuf, bufferLength - 1); + buffer[bufferLength-1] = '\n'; + return true; } }