1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 02:08:27 +08:00

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
This commit is contained in:
Brad King
2025-05-06 11:28:52 -04:00
parent dd835c8b2b
commit cef4676d3a
13 changed files with 160 additions and 59 deletions

View File

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

View File

@@ -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;

View File

@@ -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();

View File

@@ -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
Source/cmStdIoInit.cxx Normal file
View File

@@ -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
Source/cmStdIoInit.h Normal file
View File

@@ -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;
};
}
}

View File

@@ -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. */

View File

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

View File

@@ -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>();

View File

@@ -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;

View File

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

View File

@@ -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({});
}

View File

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