Browse Source

StdIo: Factor out helper to initialize stdin, stdout, and stderr

Move logic from commit c85524a94a (Ensure stdin, stdout, and stderr pipes
are always open, 2019-05-02, v3.15.0-rc1~171^2) and commit 96010cc968
(Ensure stdin, stdout, stderr FILE streams are open on Windows, 2024-01-24,
v3.29.0-rc1~65^2) to a dedicated source.  Expose it through an `Init` class
constructor to make it optionally available during static initialization.

Issue: #26924
Brad King 5 months ago
parent
commit
cef4676d3a

+ 2 - 0
Source/CMakeLists.txt

@@ -468,6 +468,8 @@ add_library(
   cmStateSnapshot.cxx
   cmStateSnapshot.h
   cmStateTypes.h
+  cmStdIoInit.h
+  cmStdIoInit.cxx
   cmStringAlgorithms.cxx
   cmStringAlgorithms.h
   cmSyntheticTargetCache.h

+ 2 - 1
Source/CPack/cpack.cxx

@@ -33,6 +33,7 @@
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
+#include "cmStdIoInit.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmValue.h"
@@ -89,7 +90,7 @@ std::vector<cmDocumentationEntry> makeGeneratorDocs(
 // this is CPack.
 int main(int argc, char const* const* argv)
 {
-  cmSystemTools::EnsureStdPipes();
+  cm::StdIo::Init();
 
   // Replace streambuf so we can output Unicode to console
   cmConsoleBuf consoleBuf;

+ 3 - 1
Source/CursesDialog/ccmake.cxx

@@ -19,6 +19,7 @@
 #include "cmDocumentationEntry.h"
 #include "cmMessageMetadata.h"
 #include "cmState.h"
+#include "cmStdIoInit.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
@@ -62,7 +63,8 @@ cmCursesForm* cmCursesForm::CurrentForm = nullptr;
 
 int main(int argc, char const* const* argv)
 {
-  cmSystemTools::EnsureStdPipes();
+  cm::StdIo::Init();
+
   cmsys::Encoding::CommandLineArguments encoding_args =
     cmsys::Encoding::CommandLineArguments::Main(argc, argv);
   argc = encoding_args.argc();

+ 3 - 1
Source/QtDialog/CMakeSetup.cxx

@@ -18,6 +18,7 @@
 #include "cmAlgorithms.h"
 #include "cmDocumentation.h"
 #include "cmDocumentationEntry.h"
+#include "cmStdIoInit.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h" // IWYU pragma: keep
 #include "cmake.h"
@@ -66,7 +67,8 @@ void OpenReferenceManual(QString const& filename);
 
 int main(int argc, char** argv)
 {
-  cmSystemTools::EnsureStdPipes();
+  cm::StdIo::Init();
+
   cmsys::Encoding::CommandLineArguments encoding_args =
     cmsys::Encoding::CommandLineArguments::Main(argc, argv);
   int argc2 = encoding_args.argc();

+ 102 - 0
Source/cmStdIoInit.cxx

@@ -0,0 +1,102 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file LICENSE.rst or https://cmake.org/licensing for details.  */
+#include "cmStdIoInit.h"
+
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+
+#include <fcntl.h>
+
+#ifdef _WIN32
+#  include <windows.h>
+
+#  include <io.h> // for _close, _dup2, _get_osfhandle
+
+#  include "cm_fileno.hxx"
+#else
+#  include <unistd.h>
+#endif
+
+namespace cm {
+namespace StdIo {
+
+namespace {
+
+#ifdef _WIN32
+void InitStdPipe(int stdFd, DWORD nStdHandle, FILE* stream,
+                 wchar_t const* mode)
+{
+  if (cm_fileno(stream) >= 0) {
+    return;
+  }
+  _close(stdFd);
+  _wfreopen(L"NUL", mode, stream);
+  int fd = cm_fileno(stream);
+  if (fd < 0) {
+    perror("failed to open NUL for missing stdio pipe");
+    abort();
+  }
+  if (fd != stdFd) {
+    _dup2(fd, stdFd);
+  }
+  SetStdHandle(nStdHandle, reinterpret_cast<HANDLE>(_get_osfhandle(fd)));
+}
+#else
+void InitStdPipe(int fd)
+{
+  if (fcntl(fd, F_GETFD) != -1 || errno != EBADF) {
+    return;
+  }
+
+  int f = open("/dev/null", fd == STDIN_FILENO ? O_RDONLY : O_WRONLY);
+  if (f == -1) {
+    perror("failed to open /dev/null for missing stdio pipe");
+    abort();
+  }
+  if (f != fd) {
+    dup2(f, fd);
+    close(f);
+  }
+}
+#endif
+
+struct InitStdPipes
+{
+  InitStdPipes()
+  {
+#ifdef _WIN32
+    InitStdPipe(0, STD_INPUT_HANDLE, stdin, L"rb");
+    InitStdPipe(1, STD_OUTPUT_HANDLE, stdout, L"wb");
+    InitStdPipe(2, STD_ERROR_HANDLE, stderr, L"wb");
+#else
+    InitStdPipe(STDIN_FILENO);
+    InitStdPipe(STDOUT_FILENO);
+    InitStdPipe(STDERR_FILENO);
+#endif
+  }
+};
+
+} // anonymous namespace
+
+class Globals
+{
+public:
+  InitStdPipes InitPipes;
+
+  static Globals& Get();
+};
+
+Globals& Globals::Get()
+{
+  static Globals globals;
+  return globals;
+}
+
+Init::Init()
+{
+  Globals::Get();
+}
+
+}
+}

+ 27 - 0
Source/cmStdIoInit.h

@@ -0,0 +1,27 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file LICENSE.rst or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+namespace cm {
+namespace StdIo {
+
+/**
+ * Initialize process-wide `stdin`, `stdout`, and `stderr` streams.
+ * After construction, standard in/out/err descriptors/handles are open,
+ * and standard `FILE*` streams from `<cstdio>` are associated with them.
+ */
+class Init
+{
+public:
+  Init();
+  ~Init() = default;
+  Init(Init&&) noexcept = default;
+  Init(Init const&) = delete;
+  Init& operator=(Init&&) noexcept = default;
+  Init& operator=(Init const&) = delete;
+};
+
+}
+}

+ 0 - 52
Source/cmSystemTools.cxx

@@ -2866,58 +2866,6 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
   }
 }
 
-#ifdef _WIN32
-static void EnsureStdPipe(int stdFd, DWORD nStdHandle, FILE* stream,
-                          wchar_t const* mode)
-{
-  if (fileno(stream) >= 0) {
-    return;
-  }
-  _close(stdFd);
-  _wfreopen(L"NUL", mode, stream);
-  int fd = fileno(stream);
-  if (fd < 0) {
-    perror("failed to open NUL for missing stdio pipe");
-    abort();
-  }
-  if (fd != stdFd) {
-    _dup2(fd, stdFd);
-  }
-  SetStdHandle(nStdHandle, reinterpret_cast<HANDLE>(_get_osfhandle(fd)));
-}
-
-void cmSystemTools::EnsureStdPipes()
-{
-  EnsureStdPipe(0, STD_INPUT_HANDLE, stdin, L"rb");
-  EnsureStdPipe(1, STD_OUTPUT_HANDLE, stdout, L"wb");
-  EnsureStdPipe(2, STD_ERROR_HANDLE, stderr, L"wb");
-}
-#else
-static void EnsureStdPipe(int fd)
-{
-  if (fcntl(fd, F_GETFD) != -1 || errno != EBADF) {
-    return;
-  }
-
-  int f = open("/dev/null", fd == STDIN_FILENO ? O_RDONLY : O_WRONLY);
-  if (f == -1) {
-    perror("failed to open /dev/null for missing stdio pipe");
-    abort();
-  }
-  if (f != fd) {
-    dup2(f, fd);
-    close(f);
-  }
-}
-
-void cmSystemTools::EnsureStdPipes()
-{
-  EnsureStdPipe(STDIN_FILENO);
-  EnsureStdPipe(STDOUT_FILENO);
-  EnsureStdPipe(STDERR_FILENO);
-}
-#endif
-
 #ifdef _WIN32
 #  ifndef CRYPT_SILENT
 #    define CRYPT_SILENT 0x40 /* Not defined by VS 6 version of header.  */

+ 0 - 2
Source/cmSystemTools.h

@@ -554,8 +554,6 @@ public:
                          cmTarExtractTimestamps extractTimestamps,
                          bool verbose);
 
-  static void EnsureStdPipes();
-
   /** Random number generation.  */
   static unsigned int RandomSeed();
   static unsigned int RandomNumber();

+ 2 - 1
Source/cmakemain.cxx

@@ -35,6 +35,7 @@
 #include "cmMessageMetadata.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
+#include "cmStdIoInit.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmValue.h"
@@ -1142,7 +1143,7 @@ int do_open(int ac, char const* const* av)
 
 int main(int ac, char const* const* av)
 {
-  cmSystemTools::EnsureStdPipes();
+  cm::StdIo::Init();
 
   // Replace streambuf so we can output Unicode to console
   auto consoleBuf = cm::make_unique<cmConsoleBuf>();

+ 2 - 1
Source/ctest.cxx

@@ -14,6 +14,7 @@
 #include "cmDocumentationEntry.h"
 #include "cmInstrumentation.h"
 #include "cmInstrumentationQuery.h"
+#include "cmStdIoInit.h"
 #include "cmSystemTools.h"
 
 #include "CTest/cmCTestLaunch.h"
@@ -166,7 +167,7 @@ cmDocumentationEntry const cmDocumentationOptions[] = {
 // this is a test driver program for cmCTest.
 int main(int argc, char const* const* argv)
 {
-  cmSystemTools::EnsureStdPipes();
+  cm::StdIo::Init();
 
   // Replace streambuf so we can output Unicode to console
   cmConsoleBuf consoleBuf;

+ 1 - 0
Tests/CMakeLib/CMakeLists.txt

@@ -24,6 +24,7 @@ set(CMakeLib_TESTS
   testRange.cxx
   testOptional.cxx
   testPathResolver.cxx
+  testStdIo.cxx
   testString.cxx
   testStringAlgorithms.cxx
   testSystemTools.cxx

+ 15 - 0
Tests/CMakeLib/testStdIo.cxx

@@ -0,0 +1,15 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file LICENSE.rst or https://cmake.org/licensing for details.  */
+
+#include "cmStdIoInit.h"
+
+#include "testCommon.h"
+
+namespace {
+}
+
+int testStdIo(int /*unused*/, char* /*unused*/[])
+{
+  cm::StdIo::Init();
+  return runTests({});
+}

+ 1 - 0
bootstrap

@@ -489,6 +489,7 @@ CMAKE_CXX_SOURCES="\
   cmState \
   cmStateDirectory \
   cmStateSnapshot \
+  cmStdIoInit \
   cmString \
   cmStringAlgorithms \
   cmStringReplaceHelper \