2020/02/16

Unreal 4.2x 이후 Visual Studio 빌드 오류 인코딩 깨짐

집에서 설치판으로 가지고 놀다보니 답답하길래 대충 고쳤다.
utf8과 ascii를 섞어쓰는 양코백이 놈들이 잘못했다.

2020/02/16
4.24.2 설치형 + VS2019 기준으로 작성

Unreal Build Tool에서 입력 출력이 틀려먹었는데,
정확히는 DotNETUtilities에서 잘못 하고 있는게 맞다.

1. 출력
UBT 실행 결과를 VS로 던질 때 UTF-8로 던져야 한다.
Engine\Source\Programs\DotNETCommon\DotNETUtilities\Log.cs:236

  /// 
  /// Static initializer
  /// 
  static Log()
  {
   Indent = "";
   OutputLevel = LogEventType.Log;
   IncludeSeverityPrefix = true;
   IncludeProgramNameWithSeverityPrefix = false;
   IncludeCallingMethod = true;
   IncludeCallingMethodForConsole = false;
   ColorConsoleOutput = true;
   // set output encoding as utf8
   Console.OutputEncoding = Encoding.UTF8;
  }

2. 입력
컴파일러를 DotNETUtil의 ManagedProcess를 사용해 병렬 처리를 하는데,
실행되는 프로세스의 인코딩을 Console.OutputEncoding과 일치한다고 생각하고 사용하고 있다.
아쉽게도 우리는 cp949로 실행된다.

그래서 일단 두 군데를 고치는데,
ManagedProcess가 초기화 될 때 ReadStream 의 인코딩을 Encoding.Default로 지정해주고,
ReadAllLines에서 ReadStream.CurrentEncoding 을 이용해서 문자열을 해석 하도록 할 것이다.


UE_4.24\Engine\Source\Programs\DotNETCommon\DotNETUtilities\ManagedProcess.cs
CreateManagedProcessWin32 함수 .. 

     // Create the stream objects for reading the process output
     InnerStream = new FileStream(StdOutRead, FileAccess.Read, 4096, false);
     // set to default stream
     ReadStream = new StreamReader(InnerStream, Encoding.Default);

ReadAllLines 함수 ..

  /// 
  /// Read all the output from the process. Does not return until the process terminates.
  /// 
  /// List of output lines
  public List ReadAllLines()
  {
   // Manually read all the output lines from the stream. Using ReadToEndAsync() or ReadAsync() on the StreamReader is abysmally slow, especially
   // for high-volume processes. Manually picking out the lines via a buffered ReadAsync() call was found to be 6x faster on 'p4 -ztag have' calls.
   List OutputLines = new List();
   byte[] Buffer = new byte[32 * 1024];
   byte LastCharacter = 0;
   int NumBytesInBuffer = 0;
   for(;;)
   {
    // If we're got a single line larger than 32kb (!), enlarge the buffer to ensure we can handle it
    if(NumBytesInBuffer == Buffer.Length)
    {
     Array.Resize(ref Buffer, Buffer.Length + 32 * 1024);
    }

    // Fill the buffer, reentering managed code every 20ms to allow thread abort exceptions to be thrown
    int NumBytesRead = Read(Buffer, NumBytesInBuffer, Buffer.Length - NumBytesInBuffer);
    if(NumBytesRead == 0)
    {
     if(NumBytesInBuffer > 0)
     {
      // consider input encoding as default (or anything)
      OutputLines.Add(ReadStream.CurrentEncoding.GetString(Buffer, 0, NumBytesInBuffer));
     }
     break;
    }

    // Otherwise append to the existing buffer
    NumBytesInBuffer += NumBytesRead;

    // Pull out all the complete output lines
    int LastStartIdx = 0;
    for(int Idx = 0; Idx < NumBytesInBuffer; Idx++)
    {
     if(Buffer[Idx] == '\r' || Buffer[Idx] == '\n')
     {
      if(Buffer[Idx] != '\n' || LastCharacter != '\r')
      {
       // consider input encoding as default (or anything)
       OutputLines.Add(ReadStream.CurrentEncoding.GetString(Buffer, LastStartIdx, Idx - LastStartIdx));
      }
      LastStartIdx = Idx + 1;
     }
     LastCharacter = Buffer[Idx];
    }

    // Shuffle everything back to the start of the buffer
    Array.Copy(Buffer, LastStartIdx, Buffer, 0, Buffer.Length - LastStartIdx);
    NumBytesInBuffer -= LastStartIdx;
   }
   return OutputLines;
  } 

이렇게 하면 나중에 readstream encoding만 변경하면 readalllines 도 같이 바뀌겠지
뭔가 조금 더 정리해서 적고 싶지만 졸리니까 나중에..

어쨌든 이걸 적용하면 UBT 등등 dotnetutils를 사용해서 외부 프로세스를 띄운 다음 로그를 받아서 에디터나 vs로 띄우는 과정에서 생기는 인코딩 오류가 전체적으로 사라질 것인다.
물론 외부 프로세스 자체가 utf-8 출력을 지원하는 경우에 또 ManagedProcess의 기본값이 Default이기 때문에 오동작 할 수 있을거 같은데 다른 방식으로 풀 수 있지 않을까 싶음

No comments:

Post a Comment