File: BackEnd\Components\Communications\RarNodeLauncher.cs
Web Access
Project: src\msbuild\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Internal;
using Microsoft.Build.Shared;
using Microsoft.Build.Shared.FileSystem;

namespace Microsoft.Build.BackEnd
{
    internal sealed class RarNodeLauncher
    {
        private readonly INodeLauncher _nodeLauncher;

        private readonly string _pipeName;

        internal RarNodeLauncher(INodeLauncher nodeLauncher)
        {
            _nodeLauncher = nodeLauncher;
            _pipeName = NamedPipeUtil.GetRarNodePipeName(new(HandshakeOptions.None));
        }

        /// <summary>
        /// Creates a new MSBuild process with the RAR nodemode.
        /// </summary>
        public bool Start()
        {
            if (IsRarNodeRunning())
            {
                CommunicationsUtilities.Trace("Existing RAR node found.");
                return true;
            }

            CommunicationsUtilities.Trace("Launching RAR node...");

            try
            {
                LaunchNode();
            }
            catch (NodeFailedToLaunchException ex)
            {
                CommunicationsUtilities.Trace($"Failed to launch RAR node: {ex}");
                return false;
            }

            return true;
        }

        private bool IsRarNodeRunning()
        {
            // Determine if the node is running by checking if the expected named pipe exists.
            if (NativeMethodsShared.IsWindows)
            {
                const string NamedPipeRoot = @"\\.\pipe\";

                // File.Exists() will crash the pipe server, as the underlying Windows APIs have undefined behavior
                // when used with pipe objects. Enumerating the pipe directory avoids this issue.
                IEnumerable<string> pipeNames = FileSystems.Default.EnumerateFiles(NamedPipeRoot);

                return pipeNames.Contains(Path.Combine(NamedPipeRoot, _pipeName));
            }
            else
            {
                // On Unix, named pipes are implemented via sockets, and the pipe name is simply the file path.
                return FileSystems.Default.FileExists(_pipeName);
            }
        }

        private void LaunchNode()
        {
            string msbuildLocation = BuildEnvironmentHelper.Instance.CurrentMSBuildExePath;
            string commandLineArgs = string.Join(" ", ["/nologo", NodeModeHelper.ToCommandLineArgument(NodeMode.OutOfProcRarNode)]);
            _ = _nodeLauncher.Start(new NodeLaunchData(msbuildLocation, commandLineArgs), nodeId: 0);
        }
    }
}