Beispiel #1
0
        /// <summary>
        /// Appends standard properties to a dictionary. These properties are read from
        /// the registry under Windows (they are a part of a toolset definition).
        /// </summary>
        private static void AppendStandardProperties(
            PropertyDictionary <ProjectPropertyInstance> properties,
            PropertyDictionary <ProjectPropertyInstance> globalProperties,
            string version,
            string root,
            string toolsPath)
        {
#if XPLAT
            if (NativeMethodsShared.IsMono)
            {
                var v4Dir = FrameworkLocationHelper.GetPathToDotNetFrameworkV40(DotNetFrameworkArchitecture.Current)
                            + Path.DirectorySeparatorChar;
                var v35Dir = FrameworkLocationHelper.GetPathToDotNetFrameworkV35(DotNetFrameworkArchitecture.Current)
                             + Path.DirectorySeparatorChar;

                if (root == null)
                {
                    var libraryPath = NativeMethodsShared.FrameworkBasePath;
                    if (toolsPath.StartsWith(libraryPath))
                    {
                        root = Path.GetDirectoryName(toolsPath);
                        if (toolsPath.EndsWith("bin"))
                        {
                            root = Path.GetDirectoryName(root);
                        }
                    }
                    else
                    {
                        root = libraryPath;
                    }
                }

                root += Path.DirectorySeparatorChar;

                // Global properties cannot be overwritten
                if (globalProperties["FrameworkSDKRoot"] == null && properties["FrameworkSDKRoot"] == null)
                {
                    properties.Set(ProjectPropertyInstance.Create("FrameworkSDKRoot", root, true, false));
                }
                if (globalProperties["MSBuildToolsRoot"] == null && properties["MSBuildToolsRoot"] == null)
                {
                    properties.Set(ProjectPropertyInstance.Create("MSBuildToolsRoot", root, true, false));
                }
                if (globalProperties["MSBuildFrameworkToolsPath"] == null &&
                    properties["MSBuildFrameworkToolsPath"] == null)
                {
                    properties.Set(ProjectPropertyInstance.Create("MSBuildFrameworkToolsPath", toolsPath, true, false));
                }
                if (globalProperties["MSBuildFrameworkToolsPath32"] == null &&
                    properties["MSBuildFrameworkToolsPath32"] == null)
                {
                    properties.Set(
                        ProjectPropertyInstance.Create("MSBuildFrameworkToolsPath32", toolsPath, true, false));
                }
                if (globalProperties["MSBuildRuntimeVersion"] == null && properties["MSBuildRuntimeVersion"] == null)
                {
                    properties.Set(ProjectPropertyInstance.Create("MSBuildRuntimeVersion", version, true, false));
                }
                if (!string.IsNullOrEmpty(v35Dir) && globalProperties["SDK35ToolsPath"] == null &&
                    properties["SDK35ToolsPath"] == null)
                {
                    properties.Set(ProjectPropertyInstance.Create("SDK35ToolsPath", v35Dir, true, false));
                }
                if (!string.IsNullOrEmpty(v4Dir) && globalProperties["SDK40ToolsPath"] == null &&
                    properties["SDK40ToolsPath"] == null)
                {
                    properties.Set(ProjectPropertyInstance.Create("SDK40ToolsPath", v4Dir, true, false));
                }
            }
#endif
        }
Beispiel #2
0
        /// <summary>
        /// Finds or creates a child process which can act as a node.
        /// </summary>
        /// <returns>The pipe stream representing the node.</returns>
        protected NodeContext GetNode(string msbuildLocation, string commandLineArgs, int nodeId, INodePacketFactory factory, long hostHandshake, long clientHandshake, NodeContextTerminateDelegate terminateNode)
        {
            if (String.IsNullOrEmpty(msbuildLocation))
            {
                msbuildLocation = _componentHost.BuildParameters.NodeExeLocation;
            }

            if (String.IsNullOrEmpty(msbuildLocation))
            {
                string msbuildExeName = Environment.GetEnvironmentVariable("MSBUILD_EXE_NAME");

                if (!String.IsNullOrEmpty(msbuildExeName))
                {
                    // we assume that MSBUILD_EXE_NAME is, in fact, just the name.
                    msbuildLocation = Path.Combine(msbuildExeName, ".exe");
                }
            }

            if (String.IsNullOrEmpty(msbuildLocation))
            {
                msbuildLocation = "MSBuild.exe";
            }

            string msbuildName = Path.GetFileNameWithoutExtension(msbuildLocation);

            List <Process> nodeProcesses = new List <Process>(Process.GetProcessesByName(msbuildName));

            // Trivial sort to try to prefer most recently used nodes
            nodeProcesses.Sort
            (
                delegate(Process left, Process right)
            {
                return(left.Id - right.Id);
            }

            );

            CommunicationsUtilities.Trace("Attempting to connect to each existing msbuild.exe process in turn to establish node {0}...", nodeId);
            foreach (Process nodeProcess in nodeProcesses)
            {
                if (nodeProcess.Id == Process.GetCurrentProcess().Id)
                {
                    continue;
                }

                // Get the full context of this inspection so that we can always skip this process when we have the same taskhost context
                string nodeLookupKey = GetProcessesToIgnoreKey(hostHandshake, clientHandshake, nodeProcess.Id);
                if (_processesToIgnore.Contains(nodeLookupKey))
                {
                    continue;
                }

                // We don't need to check this again
                _processesToIgnore.Add(nodeLookupKey);

                // Attempt to connect to each process in turn.
                NamedPipeClientStream nodeStream = TryConnectToProcess(nodeProcess.Id, 0 /* poll, don't wait for connections */, hostHandshake, clientHandshake);
                if (nodeStream != null)
                {
                    // Connection successful, use this node.
                    CommunicationsUtilities.Trace("Successfully connected to existed node {0} which is PID {1}", nodeId, nodeProcess.Id);
                    return(new NodeContext(nodeId, nodeProcess.Id, nodeStream, factory, terminateNode));
                }
            }

            // None of the processes we tried to connect to allowed a connection, so create a new one.
            // We try this in a loop because it is possible that there is another MSBuild multiproc
            // host process running somewhere which is also trying to create nodes right now.  It might
            // find our newly created node and connect to it before we get a chance.
            CommunicationsUtilities.Trace("Could not connect to existing process, now creating a process...");
            int retries = NodeCreationRetries;

            while (retries-- > 0)
            {
                // We will also check to see if .NET 3.5 is installed in the case where we need to launch a CLR2 OOP TaskHost.
                // Failure to detect this has been known to stall builds when Windows pops up a related dialog.
                // It's also a waste of time when we attempt several times to launch multiple MSBuildTaskHost.exe (CLR2 TaskHost)
                // nodes because we should never be able to connect in this case.
                string taskHostNameForClr2TaskHost = Path.GetFileNameWithoutExtension(NodeProviderOutOfProcTaskHost.TaskHostNameForClr2TaskHost);
                if (msbuildName.Equals(taskHostNameForClr2TaskHost, StringComparison.OrdinalIgnoreCase))
                {
                    if (FrameworkLocationHelper.GetPathToDotNetFrameworkV35(DotNetFrameworkArchitecture.Current) == null)
                    {
                        CommunicationsUtilities.Trace
                        (
                            "Failed to launch node from {0}. The required .NET Framework v3.5 is not installed or enabled. CommandLine: {1}",
                            msbuildLocation,
                            commandLineArgs
                        );

                        string nodeFailedToLaunchError = ResourceUtilities.FormatResourceString("TaskHostNodeFailedToLaunchErrorCodeNet35NotInstalled");
                        throw new NodeFailedToLaunchException(null, nodeFailedToLaunchError);
                    }
                }

                // Create the node process
                int msbuildProcessId = LaunchNode(msbuildLocation, commandLineArgs);
                _processesToIgnore.Add(GetProcessesToIgnoreKey(hostHandshake, clientHandshake, msbuildProcessId));

                // Note, when running under IMAGEFILEEXECUTIONOPTIONS registry key to debug, the process ID
                // gotten back from CreateProcess is that of the debugger, which causes this to try to connect
                // to the debugger process. Instead, use MSBUILDDEBUGONSTART=1

                // Now try to connect to it.
                NamedPipeClientStream nodeStream = TryConnectToProcess(msbuildProcessId, TimeoutForNewNodeCreation, hostHandshake, clientHandshake);
                if (nodeStream != null)
                {
                    // Connection successful, use this node.
                    CommunicationsUtilities.Trace("Successfully connected to created node {0} which is PID {1}", nodeId, msbuildProcessId);
                    return(new NodeContext(nodeId, msbuildProcessId, nodeStream, factory, terminateNode));
                }
            }

            // We were unable to launch a node.
            CommunicationsUtilities.Trace("FAILED TO CONNECT TO A CHILD NODE");
            return(null);
        }
Beispiel #3
0
        /// <summary>
        /// Gathers toolset data from the registry and configuration file, if any.
        /// NOTE:  this method is internal for unit testing purposes only.
        /// </summary>
        internal static string ReadAllToolsets
        (
            Dictionary <string, Toolset> toolsets,
#if FEATURE_WIN32_REGISTRY
            ToolsetRegistryReader registryReader,
#endif
#if FEATURE_SYSTEM_CONFIGURATION
            ToolsetConfigurationReader configurationReader,
#endif
            PropertyDictionary <ProjectPropertyInstance> environmentProperties,
            PropertyDictionary <ProjectPropertyInstance> globalProperties,
            ToolsetDefinitionLocations locations
        )
        {
            var initialProperties =
                new PropertyDictionary <ProjectPropertyInstance>(environmentProperties);

            initialProperties.ImportProperties(globalProperties);

            // The ordering here is important because the configuration file should have greater precedence
            // than the registry, and we do a check and don't read in the new toolset if there's already one.
            string defaultToolsVersionFromConfiguration         = null;
            string overrideTasksPathFromConfiguration           = null;
            string defaultOverrideToolsVersionFromConfiguration = null;

#if FEATURE_SYSTEM_CONFIGURATION
            if ((locations & ToolsetDefinitionLocations.ConfigurationFile) == ToolsetDefinitionLocations.ConfigurationFile)
            {
                if (configurationReader == null)
                {
                    configurationReader = new ToolsetConfigurationReader(environmentProperties, globalProperties);
                }

                // Accumulation of properties is okay in the config file because it's deterministically ordered
                defaultToolsVersionFromConfiguration = configurationReader.ReadToolsets(toolsets, globalProperties,
                                                                                        initialProperties, true /* accumulate properties */, out overrideTasksPathFromConfiguration,
                                                                                        out defaultOverrideToolsVersionFromConfiguration);
            }
#endif

            string defaultToolsVersionFromRegistry         = null;
            string overrideTasksPathFromRegistry           = null;
            string defaultOverrideToolsVersionFromRegistry = null;

            if ((locations & ToolsetDefinitionLocations.Registry) == ToolsetDefinitionLocations.Registry)
            {
#if FEATURE_WIN32_REGISTRY
                if (NativeMethodsShared.IsWindows || registryReader != null)
                {
                    // If we haven't been provided a registry reader (i.e. unit tests), create one
                    registryReader = registryReader ?? new ToolsetRegistryReader(environmentProperties, globalProperties);

                    // We do not accumulate properties when reading them from the registry, because the order
                    // in which values are returned to us is essentially random: so we disallow one property
                    // in the registry to refer to another also in the registry
                    defaultToolsVersionFromRegistry = registryReader.ReadToolsets(toolsets, globalProperties,
                                                                                  initialProperties, false /* do not accumulate properties */, out overrideTasksPathFromRegistry,
                                                                                  out defaultOverrideToolsVersionFromRegistry);
                }
                else
#endif
                {
                    var currentDir = BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory.TrimEnd(Path.DirectorySeparatorChar);
                    var props      = new PropertyDictionary <ProjectPropertyInstance>();

                    var libraryPath = NativeMethodsShared.FrameworkBasePath;

                    if (!string.IsNullOrEmpty(libraryPath))
                    {
                        // The 4.0 toolset is installed in the framework directory
                        var v4Dir =
                            FrameworkLocationHelper.GetPathToDotNetFrameworkV40(DotNetFrameworkArchitecture.Current);
                        if (v4Dir != null && !toolsets.ContainsKey("4.0"))
                        {
                            // Create standard properties. On Mono they are well known
                            var buildProperties =
                                CreateStandardProperties(globalProperties, "4.0", libraryPath, v4Dir);

                            toolsets.Add(
                                "4.0",
                                new Toolset(
                                    "4.0",
                                    v4Dir,
                                    buildProperties,
                                    environmentProperties,
                                    globalProperties,
                                    null,
                                    currentDir,
                                    string.Empty));
                        }

                        // Other toolsets are installed in the xbuild directory
                        var xbuildToolsetsDir = Path.Combine(libraryPath, $"xbuild{Path.DirectorySeparatorChar}");
                        if (Directory.Exists(xbuildToolsetsDir))
                        {
                            var r = new Regex(Regex.Escape(xbuildToolsetsDir) + @"\d+\.\d+");
                            foreach (var d in Directory.GetDirectories(xbuildToolsetsDir).Where(d => r.IsMatch(d)))
                            {
                                var version = Path.GetFileName(d);
                                var binPath = Path.Combine(d, "bin");
                                if (toolsets.ContainsKey(version))
                                {
                                    continue;
                                }
                                // Create standard properties. On Mono they are well known
                                var buildProperties =
                                    CreateStandardProperties(globalProperties, version, xbuildToolsetsDir, binPath);

                                toolsets.Add(
                                    version,
                                    new Toolset(
                                        version,
                                        binPath,
                                        buildProperties,
                                        environmentProperties,
                                        globalProperties,
                                        null,
                                        currentDir,
                                        string.Empty));
                            }
                        }
                    }
                    if (!toolsets.ContainsKey(MSBuildConstants.CurrentToolsVersion))
                    {
                        toolsets.Add(
                            MSBuildConstants.CurrentToolsVersion,
                            new Toolset(
                                MSBuildConstants.CurrentToolsVersion,
                                currentDir,
                                props,
                                new PropertyDictionary <ProjectPropertyInstance>(),
                                currentDir,
                                string.Empty));
                    }
                }
            }

            // The 2.0 .NET Framework installer did not write a ToolsVersion key for itself in the registry.
            // The 3.5 installer writes one for 2.0, but 3.5 might not be installed.
            // The 4.0 and subsequent installers can't keep writing the 2.0 one, because (a) it causes SxS issues and (b) we
            // don't want it unless 2.0 is installed.
            // So if the 2.0 framework is actually installed, we're reading the registry, and either the registry or the config
            // file have not already created the 2.0 toolset, mock up a fake one.
            if (((locations & ToolsetDefinitionLocations.Registry) != 0) && !toolsets.ContainsKey("2.0") &&
                FrameworkLocationHelper.PathToDotNetFrameworkV20 != null)
            {
                var synthetic20Toolset = new Toolset(
                    "2.0",
                    FrameworkLocationHelper.PathToDotNetFrameworkV20,
                    environmentProperties,
                    globalProperties,
                    null /* 2.0 did not have override tasks */,
                    null /* 2.0 did not have a default override toolsversion */);
                toolsets.Add("2.0", synthetic20Toolset);
            }

            string defaultToolsVersionFromLocal         = null;
            string overrideTasksPathFromLocal           = null;
            string defaultOverrideToolsVersionFromLocal = null;

            if ((locations & ToolsetDefinitionLocations.Local) == ToolsetDefinitionLocations.Local)
            {
                var localReader = new ToolsetLocalReader(environmentProperties, globalProperties);

                defaultToolsVersionFromLocal = localReader.ReadToolsets(
                    toolsets,
                    globalProperties,
                    initialProperties,
                    false /* accumulate properties */,
                    out overrideTasksPathFromLocal,
                    out defaultOverrideToolsVersionFromLocal);
            }

            // We'll use the path from the configuration file if it was specified, otherwise we'll try
            // the one from the registry.  It's possible (and valid) that neither the configuration file
            // nor the registry specify a override in which case we'll just return null.
            var overrideTasksPath = overrideTasksPathFromConfiguration ?? overrideTasksPathFromRegistry ?? overrideTasksPathFromLocal;

            // We'll use the path from the configuration file if it was specified, otherwise we'll try
            // the one from the registry.  It's possible (and valid) that neither the configuration file
            // nor the registry specify a override in which case we'll just return null.
            var defaultOverrideToolsVersion = defaultOverrideToolsVersionFromConfiguration
                                              ?? defaultOverrideToolsVersionFromRegistry
                                              ?? defaultOverrideToolsVersionFromLocal;

            // We'll use the default from the configuration file if it was specified, otherwise we'll try
            // the one from the registry.  It's possible (and valid) that neither the configuration file
            // nor the registry specify a default, in which case we'll just return null.
            var defaultToolsVersion = defaultToolsVersionFromConfiguration ?? defaultToolsVersionFromRegistry ?? defaultToolsVersionFromLocal;

            // If we got a default version from the registry or config file, and it
            // actually exists, fine.
            // Otherwise we have to come up with one.
            if (defaultToolsVersion != null && toolsets.ContainsKey(defaultToolsVersion))
            {
                return(defaultToolsVersion);
            }
            // We're going to choose a hard coded default tools version of 2.0.
            defaultToolsVersion = Constants.defaultToolsVersion;

            // But don't overwrite any existing tools path for this default we're choosing.
            if (toolsets.ContainsKey(Constants.defaultToolsVersion))
            {
                return(defaultToolsVersion);
            }
            // There's no tools path already for 2.0, so use the path to the v2.0 .NET Framework.
            // If an old-fashioned caller sets BinPath property, or passed a BinPath to the constructor,
            // that will overwrite what we're setting here.
            ErrorUtilities.VerifyThrow(
                Constants.defaultToolsVersion == "2.0",
                "Getting 2.0 FX path so default should be 2.0");
            var pathToFramework = FrameworkLocationHelper.PathToDotNetFrameworkV20;

            // We could not find the default toolsversion because it was not installed on the machine. Fallback to the
            // one we expect to always be there when running msbuild 4.0.
            if (pathToFramework == null)
            {
                pathToFramework     = FrameworkLocationHelper.PathToDotNetFrameworkV40;
                defaultToolsVersion = Constants.defaultFallbackToolsVersion;
            }

            // Again don't overwrite any existing tools path for this default we're choosing.
            if (toolsets.ContainsKey(defaultToolsVersion))
            {
                return(defaultToolsVersion);
            }
            var defaultToolset = new Toolset(
                defaultToolsVersion,
                pathToFramework,
                environmentProperties,
                globalProperties,
                overrideTasksPath,
                defaultOverrideToolsVersion);
            toolsets.Add(defaultToolsVersion, defaultToolset);

            return(defaultToolsVersion);
        }
Beispiel #4
0
        /// <summary>
        /// Finds or creates a child process which can act as a node.
        /// </summary>
        /// <returns>The pipe stream representing the node.</returns>
        protected NodeContext GetNode(string msbuildLocation, string commandLineArgs, int nodeId, INodePacketFactory factory, long hostHandshake, long clientHandshake, NodeContextTerminateDelegate terminateNode)
        {
#if DEBUG
            if (Execution.BuildManager.WaitForDebugger)
            {
                commandLineArgs += " /wfd";
            }
#endif

            if (String.IsNullOrEmpty(msbuildLocation))
            {
                msbuildLocation = _componentHost.BuildParameters.NodeExeLocation;
            }

            if (String.IsNullOrEmpty(msbuildLocation))
            {
                string msbuildExeName = Environment.GetEnvironmentVariable("MSBUILD_EXE_NAME");

                if (!String.IsNullOrEmpty(msbuildExeName))
                {
                    // we assume that MSBUILD_EXE_NAME is, in fact, just the name.
                    msbuildLocation = Path.Combine(msbuildExeName, ".exe");
                }
            }

            if (String.IsNullOrEmpty(msbuildLocation))
            {
                msbuildLocation = "MSBuild.exe";
            }

#if FEATURE_NODE_REUSE
            var candidateProcesses = GetPossibleRunningNodes(msbuildLocation);

            CommunicationsUtilities.Trace("Attempting to connect to each existing msbuild.exe process in turn to establish node {0}...", nodeId);
            foreach (Process nodeProcess in candidateProcesses)
            {
                if (nodeProcess.Id == Process.GetCurrentProcess().Id)
                {
                    continue;
                }

                // Get the full context of this inspection so that we can always skip this process when we have the same taskhost context
                string nodeLookupKey = GetProcessesToIgnoreKey(hostHandshake, clientHandshake, nodeProcess.Id);
                if (_processesToIgnore.Contains(nodeLookupKey))
                {
                    continue;
                }

                // We don't need to check this again
                _processesToIgnore.Add(nodeLookupKey);

                // Attempt to connect to each process in turn.
                Stream nodeStream = TryConnectToProcess(nodeProcess.Id, 0 /* poll, don't wait for connections */, hostHandshake, clientHandshake);
                if (nodeStream != null)
                {
                    // Connection successful, use this node.
                    CommunicationsUtilities.Trace("Successfully connected to existed node {0} which is PID {1}", nodeId, nodeProcess.Id);
                    return(new NodeContext(nodeId, nodeProcess.Id, nodeStream, factory, terminateNode));
                }
            }
#endif

            // None of the processes we tried to connect to allowed a connection, so create a new one.
            // We try this in a loop because it is possible that there is another MSBuild multiproc
            // host process running somewhere which is also trying to create nodes right now.  It might
            // find our newly created node and connect to it before we get a chance.
            CommunicationsUtilities.Trace("Could not connect to existing process, now creating a process...");
            int retries = NodeCreationRetries;
            while (retries-- > 0)
            {
#if FEATURE_NET35_TASKHOST
                // We will also check to see if .NET 3.5 is installed in the case where we need to launch a CLR2 OOP TaskHost.
                // Failure to detect this has been known to stall builds when Windows pops up a related dialog.
                // It's also a waste of time when we attempt several times to launch multiple MSBuildTaskHost.exe (CLR2 TaskHost)
                // nodes because we should never be able to connect in this case.
                string taskHostNameForClr2TaskHost = Path.GetFileNameWithoutExtension(NodeProviderOutOfProcTaskHost.TaskHostNameForClr2TaskHost);
                if (Path.GetFileNameWithoutExtension(msbuildLocation).Equals(taskHostNameForClr2TaskHost, StringComparison.OrdinalIgnoreCase))
                {
                    if (FrameworkLocationHelper.GetPathToDotNetFrameworkV35(DotNetFrameworkArchitecture.Current) == null)
                    {
                        CommunicationsUtilities.Trace
                        (
                            "Failed to launch node from {0}. The required .NET Framework v3.5 is not installed or enabled. CommandLine: {1}",
                            msbuildLocation,
                            commandLineArgs
                        );

                        string nodeFailedToLaunchError = ResourceUtilities.GetResourceString("TaskHostNodeFailedToLaunchErrorCodeNet35NotInstalled");
                        throw new NodeFailedToLaunchException(null, nodeFailedToLaunchError);
                    }
                }
#endif
#if !FEATURE_NAMED_PIPES_FULL_DUPLEX
                var clientToServerStream = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);
                var serverToClientStream = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable);

                commandLineArgs += $" /clientToServerPipeHandle:{clientToServerStream.GetClientHandleAsString()} /serverToClientPipeHandle:{serverToClientStream.GetClientHandleAsString()}";
#endif

                // Create the node process
                int msbuildProcessId = LaunchNode(msbuildLocation, commandLineArgs);
                _processesToIgnore.Add(GetProcessesToIgnoreKey(hostHandshake, clientHandshake, msbuildProcessId));

#if FEATURE_NAMED_PIPES_FULL_DUPLEX
                // Note, when running under IMAGEFILEEXECUTIONOPTIONS registry key to debug, the process ID
                // gotten back from CreateProcess is that of the debugger, which causes this to try to connect
                // to the debugger process. Instead, use MSBUILDDEBUGONSTART=1

                // Now try to connect to it.
                Stream nodeStream = TryConnectToProcess(msbuildProcessId, TimeoutForNewNodeCreation, hostHandshake, clientHandshake);
                if (nodeStream != null)
                {
                    // Connection successful, use this node.
                    CommunicationsUtilities.Trace("Successfully connected to created node {0} which is PID {1}", nodeId, msbuildProcessId);
                    return(new NodeContext(nodeId, msbuildProcessId, nodeStream, factory, terminateNode));
                }
#else
                if (WaitForConnectionFromProcess(clientToServerStream, serverToClientStream, msbuildProcessId, hostHandshake, clientHandshake))
                {
                    // Connection successful, use this node.

                    clientToServerStream.DisposeLocalCopyOfClientHandle();
                    serverToClientStream.DisposeLocalCopyOfClientHandle();

                    CommunicationsUtilities.Trace("Successfully connected to created node {0} which is PID {1}", nodeId, msbuildProcessId);
                    return(new NodeContext(nodeId, msbuildProcessId, clientToServerStream, serverToClientStream, factory, terminateNode));
                }
#endif
            }

            // We were unable to launch a node.
            CommunicationsUtilities.Trace("FAILED TO CONNECT TO A CHILD NODE");
            return(null);
        }