public static T Parse <T>(string message, IUserInterface userInterface = null, bool throwJsonException = true) where T : CMakeMessage
        {
            try
            {
                CMakeMessage cMakeMessage;
                JObject      content = JObject.Parse(message, new JsonLoadSettings {
                    LineInfoHandling = LineInfoHandling.Ignore
                });
                userInterface?.WriteVerbose(content.ToString(Formatting.Indented));
                switch (content["type"].Value <string>())
                {
                case "hello":
                    cMakeMessage = CMakeHelloMessage.Create(content);
                    break;

                case "reply":
                    cMakeMessage = CMakeReplyMessage.Create(content);
                    break;

                case "progress":
                    cMakeMessage = CMakeProgressMessage.Create();
                    break;

                case "message":
                    cMakeMessage = CMakeMessageMessage.Create(content);
                    break;

                case "signal":
                    userInterface?.WriteVerbose("Received signal message from cmake server. Signal messages are ignored.");
                    return(null);

                default:
                    throw new FormattableException($"Unknown message type {content["type"].Value<string>()}.{Environment.NewLine}" +
                                                   $"Complete message: {message}");
                }

                if (cMakeMessage is T converted)
                {
                    return(converted);
                }
                throw new FormattableException($"Expected message of type {typeof(T)}, but message was of type  {cMakeMessage.GetType()}");
            }
            catch (JsonReaderException e)
            {
                if (throwJsonException)
                {
                    throw new FormattableException($"Error while parsing the response json{Environment.NewLine}{message}.", e);
                }

                return(null);
            }
        }
        public static async Task <CMakeConversation> Start(IProcessManager processManager,
                                                           IBinariesLocator binariesLocator,
                                                           IOutputFormatterPool formatterPool,
                                                           VirtualDirectory tempDirectory, bool isWindowsSystem,
                                                           ExecutionContext executionContext,
                                                           VirtualDirectory sourceDirectory,
                                                           VirtualDirectory binaryDirectory)
        {
            IProcess process = null;
            NamedPipeClientStream pipeClient = null;

            try
            {
                string pipeName = isWindowsSystem
                                      ? $"{tempDirectory.FullName}\\.cmakeserver"
                                      : $"/tmp/cmake-server-{Guid.NewGuid().ToByteString()}";
                string serverCommand = isWindowsSystem
                                           ? $"-E server --experimental --pipe=\"\\\\?\\pipe\\{pipeName}\""
                                           : $"-E server --experimental --pipe={pipeName}";
                process    = processManager.StartProcess(binariesLocator.GetExecutableCommand("cmake"), serverCommand, executionContext);
                pipeClient = new NamedPipeClientStream(".", pipeName,
                                                       PipeDirection.InOut, PipeOptions.Asynchronous,
                                                       TokenImpersonationLevel.Impersonation);
                pipeClient.Connect(CMakeServerTimeout);
                if (!pipeClient.IsConnected)
                {
                    throw new FormattableException("Could not connect to server");
                }

                FormatterParameters parameters = new FormatterParameters();
                parameters.Add("cmake-json", MessageFormat);
                IUserInterface jsonCmakeInterface = formatterPool.GetFormatter(parameters, executionContext);

                CMakeServerStream serverStream = new CMakeServerStream(pipeClient, executionContext);
                CMakeHelloMessage hello        = null;
                do
                {
                    foreach (string singleMessage in await serverStream.ReadMessage()
                             .TimeoutAfter(CMakeServerTimeout)
                             .ConfigureAwait(false))
                    {
                        hello = CMakeMessage.Parse <CMakeMessage>(singleMessage, jsonCmakeInterface) as CMakeHelloMessage;
                    }
                } while (hello == null);

                if (hello.SupportedProtocolVersions.All(v => v.Major != 1))
                {
                    throw new FormattableException("CMake server does not support the protocol version 1.X. " +
                                                   $"Supported versions are {string.Join(", ", hello.SupportedProtocolVersions)}");
                }

                CMakeConversation conversation = new CMakeConversation(process, pipeClient, serverStream, jsonCmakeInterface);

                await conversation.Handshake(sourceDirectory.FullName.Replace('\\', '/'),
                                             binaryDirectory.FullName.Replace('\\', '/'))
                .ConfigureAwait(false);

                await conversation.Configure().ConfigureAwait(false);

                return(conversation);
            }
            catch (Exception)
            {
                pipeClient?.Dispose();
                process?.Dispose();
                throw;
            }
        }