/// <summary> Adds the target to fetch solution configuration contents for given configuration|platform combo. </summary>
        private void AddGetSolutionConfigurationContentsTarget(ProjectInstance traversalProject)
        {
            var initialTarget = traversalProject.AddTarget(
                targetName: "GetSolutionConfigurationContents",
                condition: null,
                inputs: null,
                outputs: "$(SolutionConfigurationContents)",
                returns: null,
                keepDuplicateOutputs: null,
                dependsOnTargets: null,
                parentProjectSupportsReturnsAttribute: false);

            var property = new ProjectPropertyGroupTaskPropertyInstance(
                                                    "SolutionConfigurationContents",
                                                    "@(SolutionConfiguration->WithMetadataValue('Identity', '$(Configuration)|$(Platform)')->'%(Content)')",
                                                    string.Empty,
                                                    initialTarget.Location,
                                                    initialTarget.Location);

            initialTarget.AddProjectTargetInstanceChild(new ProjectPropertyGroupTaskInstance(
                                                            string.Empty,
                                                            initialTarget.Location,
                                                            initialTarget.Location,
                                                            new ProjectPropertyGroupTaskPropertyInstance[] { property }));
        }
        /// <summary>
        /// Adds the target which validates that the solution configuration specified by the user is supported.
        /// </summary>
        private void AddValidateSolutionConfigurationTarget(ProjectInstance traversalProject)
        {
            ProjectTargetInstance initialTarget = traversalProject.AddTarget("ValidateSolutionConfiguration", null, null, null, null, null, null, false /* legacy target returns behaviour */);

            if (_solutionFile.SolutionConfigurations.Count > 0)
            {
                ProjectTaskInstance errorTask = AddErrorWarningMessageInstance
                    (
                    initialTarget,
                    "('$(CurrentSolutionConfigurationContents)' == '') and ('$(SkipInvalidConfigurations)' != 'true')",
                    XMakeElements.error,
                    false /* do not treat as literal */,
                    "SolutionInvalidSolutionConfiguration",
                    "$(Configuration)|$(Platform)"
                    );

                ProjectTaskInstance warningTask = AddErrorWarningMessageInstance
                    (
                    initialTarget,
                    "('$(CurrentSolutionConfigurationContents)' == '') and ('$(SkipInvalidConfigurations)' == 'true')",
                    XMakeElements.warning,
                    false /* do not treat as literal */,
                    "SolutionInvalidSolutionConfiguration",
                    "$(Configuration)|$(Platform)"
                    );

                ProjectTaskInstance messageTask = AddErrorWarningMessageInstance
                    (
                    initialTarget,
                    "'$(CurrentSolutionConfigurationContents)' != ''",
                    XMakeElements.message,
                    false /* do not treat as literal */,
                    "SolutionBuildingSolutionConfiguration",
                    "$(Configuration)|$(Platform)"
                    );
            }
        }
 /// <summary>
 /// Adds the target which validates that the tools version is supported.
 /// </summary>
 private void AddValidateToolsVersionsTarget(ProjectInstance traversalProject)
 {
     ProjectTargetInstance validateToolsVersionsTarget = traversalProject.AddTarget("ValidateToolsVersions", null, null, null, null, null, null, false /* legacy target returns behaviour */);
     ProjectTaskInstance toolsVersionErrorTask = AddErrorWarningMessageInstance
         (
         validateToolsVersionsTarget,
         "'$(MSBuildToolsVersion)' == '2.0' and ('$(ProjectToolsVersion)' != '2.0' and '$(ProjectToolsVersion)' != '')",
         XMakeElements.error,
         false /* do not treat as literal */,
         "SolutionToolsVersionDoesNotSupportProjectToolsVersion",
         "$(MSBuildToolsVersion)"
         );
 }
        ///<summary>
        /// Creates the target used to build all of the references in the traversal project.
        ///</summary>
        private void AddTraversalReferencesTarget(ProjectInstance traversalProject, string targetName, string outputItem)
        {
            string outputItemAsItem = null;
            if (!String.IsNullOrEmpty(outputItem))
            {
                outputItemAsItem = "@(" + outputItem + ")";
            }

            ProjectTargetInstance target = traversalProject.AddTarget(targetName ?? "Build", String.Empty, String.Empty, outputItemAsItem, null, String.Empty, String.Empty, false /* legacy target returns behaviour */);
            AddReferencesBuildTask(traversalProject, target, targetName, outputItem);
        }
        /// <summary>
        /// Adds a traversal target which invokes a specified target on a single project.  This creates targets called "Project", "Project:Rebuild", "Project:Clean", "Project:Publish" etc.
        /// </summary>
        private void AddTraversalTargetForProject(ProjectInstance traversalProject, ProjectInSolution project, ProjectConfigurationInSolution projectConfiguration, string targetToBuild, string outputItem, bool canBuildDirectly)
        {
            string baseProjectName = ProjectInSolution.DisambiguateProjectTargetName(project.GetUniqueProjectName());
            string actualTargetName = baseProjectName;
            if (targetToBuild != null)
            {
                actualTargetName += ":" + targetToBuild;
            }

            // The output item name is the concatenation of the project name with the specified outputItem.  In the typical case, if the
            // project name is MyProject, the outputItemName will be MyProjectBuildOutput, and the outputItemAsItem will be @(MyProjectBuildOutput).
            // In the case where the project contains characters not allowed as Xml element attribute values, those characters will
            // be replaced with underscores.  In the case where MyProject is actually unrepresentable in Xml, then the
            // outputItemName would be _________BuildOutput.
            string outputItemName = null;
            string outputItemAsItem = null;
            if (!String.IsNullOrEmpty(outputItem))
            {
                outputItemName = MakeIntoSafeItemName(baseProjectName) + outputItem;
                outputItemAsItem = "@(" + outputItemName + ")";
            }

            ProjectTargetInstance targetElement = traversalProject.AddTarget(actualTargetName, null, null, outputItemAsItem, null, null, null, false /* legacy target returns behaviour */);
            if (canBuildDirectly)
            {
                AddProjectBuildTask(traversalProject, project, projectConfiguration, targetElement, targetToBuild, "@(ProjectReference)", "'%(ProjectReference.Identity)' == '" + EscapingUtilities.Escape(project.AbsolutePath) + "'", outputItemName);
            }
            else
            {
                AddMetaprojectBuildTask(traversalProject, project, targetElement, targetToBuild, outputItemName);
            }
        }
        /// <summary>
        /// Adds a target which verifies that all of the project references and configurations are valid.
        /// </summary>
        private void AddValidateProjectsTarget(ProjectInstance traversalProject, List<ProjectInSolution> projects)
        {
            ProjectTargetInstance newTarget = traversalProject.AddTarget("ValidateProjects", null, null, null, null, null, null, false /* legacy target returns behaviour */);

            foreach (ProjectInSolution project in projects)
            {
                foreach (SolutionConfigurationInSolution solutionConfiguration in _solutionFile.SolutionConfigurations)
                {
                    ProjectConfigurationInSolution projectConfiguration = null;
                    string condition = GetConditionStringForConfiguration(solutionConfiguration);

                    if (project.ProjectConfigurations.TryGetValue(solutionConfiguration.FullName, out projectConfiguration))
                    {
                        if (!projectConfiguration.IncludeInBuild)
                        {
                            ProjectTaskInstance messageTask = AddErrorWarningMessageInstance
                                (
                                newTarget,
                                condition,
                                XMakeElements.message,
                                true,
                                "SolutionProjectSkippedForBuilding",
                                project.ProjectName,
                                solutionConfiguration.FullName
                                );
                        }
                    }
                    else
                    {
                        ProjectTaskInstance warningTask = AddErrorWarningMessageInstance
                            (
                            newTarget,
                            condition,
                            XMakeElements.warning,
                            true,
                            "SolutionProjectConfigurationMissing",
                            project.ProjectName,
                            solutionConfiguration.FullName
                            );
                    }
                }
            }
        }
        /// <summary>
        /// Adds a target for a project whose type is unknown and we cannot build.  We will emit an error or warning as appropriate.
        /// </summary>
        private void AddMetaprojectTargetForUnknownProjectType(ProjectInstance traversalProject, ProjectInstance metaprojectInstance, ProjectInSolution project, string targetName, string unknownProjectTypeErrorMessage)
        {
            ProjectTargetInstance newTarget = metaprojectInstance.AddTarget(targetName ?? "Build", "'$(CurrentSolutionConfigurationContents)' != ''", null, null, null, null, null, false /* legacy target returns behaviour */);

            foreach (SolutionConfigurationInSolution solutionConfiguration in _solutionFile.SolutionConfigurations)
            {
                ProjectConfigurationInSolution projectConfiguration = null;
                ProjectTaskInstance newTask = null;

                if (project.ProjectConfigurations.TryGetValue(solutionConfiguration.FullName, out projectConfiguration))
                {
                    if (projectConfiguration.IncludeInBuild)
                    {
                        // Only add the task if it would run in this configuration.
                        if (!traversalProject.EvaluateCondition(GetConditionStringForConfiguration(solutionConfiguration)))
                        {
                            continue;
                        }

                        if (unknownProjectTypeErrorMessage == null)
                        {
                            // we haven't encountered any problems accessing the project file in the past, but do not support
                            // building this project type
                            newTask = AddErrorWarningMessageInstance
                                (
                                newTarget,
                                null,
                                XMakeElements.warning,
                                true,
                                "SolutionParseUnknownProjectType",
                                project.RelativePath
                                );
                        }
                        else
                        {
                            // this project file may be of supported type, but we have encountered problems accessing it
                            newTask = AddErrorWarningMessageInstance
                                (
                                newTarget,
                                null,
                                XMakeElements.warning,
                                true,
                                "SolutionParseErrorReadingProject",
                                project.RelativePath,
                                unknownProjectTypeErrorMessage
                                );
                        }
                    }
                    else
                    {
                        newTask = AddErrorWarningMessageInstance
                            (
                            newTarget,
                            null,
                            XMakeElements.message,
                            true,
                            "SolutionProjectSkippedForBuilding",
                            project.ProjectName,
                            solutionConfiguration.FullName
                            );
                    }
                }
                else
                {
                    newTask = AddErrorWarningMessageInstance
                        (
                        newTarget,
                        null,
                        XMakeElements.warning,
                        true,
                        "SolutionProjectConfigurationMissing",
                        project.ProjectName,
                        solutionConfiguration.FullName
                        );
                }
            }
        }
        /// <summary>
        /// Add a target to the project called "GetFrameworkPathAndRedistList".  This target calls the
        /// GetFrameworkPath task and then CreateItem to populate @(_CombinedTargetFrameworkDirectoriesItem) and
        /// @(InstalledAssemblyTables), so that we can pass these into the ResolveAssemblyReference task
        /// when building web projects.
        /// </summary>
        private void AddTargetForGetFrameworkPathAndRedistList(ProjectInstance metaprojectInstance)
        {
            if (metaprojectInstance.Targets.ContainsKey("GetFrameworkPathAndRedistList"))
            {
                return;
            }

            ProjectTargetInstance frameworkPathAndRedistListTarget = metaprojectInstance.AddTarget("GetFrameworkPathAndRedistList", String.Empty, null, null, null, null, null, false /* legacy target returns behaviour */);

            ProjectTaskInstance getFrameworkPathTask = frameworkPathAndRedistListTarget.AddTask("GetFrameworkPath", String.Empty, null);

            // Follow the same logic we use in Microsoft.Common.targets to choose the target framework
            // directories (which are then used to find the set of redist lists).
            getFrameworkPathTask.AddOutputItem(
                "Path",
                "_CombinedTargetFrameworkDirectoriesItem",
                "'$(MSBuildToolsVersion)' == '2.0'");

            // TFV v4.0 supported by TV 4.0+
            getFrameworkPathTask.AddOutputItem(
                "FrameworkVersion40Path",
                "_CombinedTargetFrameworkDirectoriesItem",
                " '$(TargetFrameworkVersion)' == 'v4.0' and '$(MSBuildToolsVersion)' != '2.0' and '$(MSBuildToolsVersion)' != '3.5'");

            // TFV v3.5 supported by TV 3.5+
            getFrameworkPathTask.AddOutputItem(
                "FrameworkVersion35Path",
                "_CombinedTargetFrameworkDirectoriesItem",
                " ('$(TargetFrameworkVersion)' == 'v3.5' or '$(TargetFrameworkVersion)' == 'v4.0') and '$(MSBuildToolsVersion)' != '2.0'");

            // TFV v3.0 supported by TV 3.5+ (there was no TV 3.0)
            getFrameworkPathTask.AddOutputItem(
                "FrameworkVersion30Path",
                "_CombinedTargetFrameworkDirectoriesItem",
                " ('$(TargetFrameworkVersion)' == 'v3.0' or '$(TargetFrameworkVersion)' == 'v3.5' or '$(TargetFrameworkVersion)' == 'v4.0') and '$(MSBuildToolsVersion)' != '2.0'");

            // TFV v2.0 supported by TV 3.5+ (there was no TV 3.0). This property was not added until toolsversion 3.5 therefore it cannot be used for toolsversion 2.0
            getFrameworkPathTask.AddOutputItem(
                "FrameworkVersion20Path",
                "_CombinedTargetFrameworkDirectoriesItem",
                "'$(MSBuildToolsVersion)' != '2.0'");

            ProjectTaskInstance createItemTask = frameworkPathAndRedistListTarget.AddTask("CreateItem", null, null);
            createItemTask.SetParameter("Include", @"@(_CombinedTargetFrameworkDirectoriesItem->'%(Identity)\RedistList\*.xml')");
            createItemTask.AddOutputItem("Include", "InstalledAssemblyTables", null);
        }
        /// <summary>
        /// Add a target for a Venus project into the XML doc that's being generated.  This
        /// target will call the AspNetCompiler task.
        /// </summary>
        private void AddMetaprojectTargetForWebProject(ProjectInstance traversalProject, ProjectInstance metaprojectInstance, ProjectInSolution project, string targetName)
        {
            // Add a supporting target called "GetFrameworkPathAndRedistList".
            AddTargetForGetFrameworkPathAndRedistList(metaprojectInstance);

            ProjectTargetInstance newTarget = metaprojectInstance.AddTarget(targetName ?? "Build", ComputeTargetConditionForWebProject(project), null, null, null, null, "GetFrameworkPathAndRedistList", false /* legacy target returns behaviour */);

            // Build the references
            AddReferencesBuildTask(metaprojectInstance, newTarget, targetName, null);

            if (targetName == "Clean")
            {
                // Well, hmmm.  The AspNetCompiler task doesn't support any kind of 
                // a "Clean" operation.  The best we can really do is offer up a 
                // message saying so.
                AddErrorWarningMessageInstance(newTarget, null, XMakeElements.message, true, "SolutionVenusProjectNoClean");
            }
            else if (targetName == "Publish")
            {
                // Well, hmmm.  The AspNetCompiler task doesn't support any kind of 
                // a "Publish" operation.  The best we can really do is offer up a 
                // message saying so.
                AddErrorWarningMessageInstance(newTarget, null, XMakeElements.message, true, "SolutionVenusProjectNoPublish");
            }
            else
            {
                // For normal build and "Rebuild", just call the AspNetCompiler task with the
                // correct parameters.  But before calling the AspNetCompiler task, we need to
                // do a bunch of prep work regarding references.

                // We're going to build up an MSBuild condition string that represents the valid Configurations.
                // We do this by OR'ing together individual conditions, each of which compares $(Configuration)
                // with a valid configuration name.  We init our condition string to "false", so we can easily 
                // OR together more stuff as we go, and also easily take the negation of the condition by putting
                // a ! around the whole thing.
                StringBuilder conditionDescribingValidConfigurations = new StringBuilder("(false)");

                // Loop through all the valid configurations and add a PropertyGroup for each one.
                foreach (DictionaryEntry aspNetConfiguration in project.AspNetConfigurations)
                {
                    string configurationName = (string)aspNetConfiguration.Key;
                    AspNetCompilerParameters aspNetCompilerParameters = (AspNetCompilerParameters)aspNetConfiguration.Value;

                    // We only add the PropertyGroup once per Venus project.  Without the following "if", we would add
                    // the same identical PropertyGroup twice, once when AddTargetForWebProject is called with 
                    // subTargetName=null and once when subTargetName="Rebuild".
                    if (targetName == null)
                    {
                        AddPropertyGroupForAspNetConfiguration(traversalProject, metaprojectInstance, project, configurationName, aspNetCompilerParameters, _solutionFile.FullPath);
                    }

                    // Update our big condition string to include this configuration.
                    conditionDescribingValidConfigurations.Append(" or ");
                    conditionDescribingValidConfigurations.Append(String.Format(CultureInfo.InvariantCulture, "('$(AspNetConfiguration)' == '{0}')", EscapingUtilities.Escape(configurationName)));
                }

                StringBuilder referenceItemName = new StringBuilder(GenerateSafePropertyName(project, "References"));
                if (!string.IsNullOrEmpty(targetName))
                {
                    referenceItemName.Append('_');
                    referenceItemName.Append(targetName);
                }

                // Add tasks to resolve project references of this web project, if any
                if (project.ProjectReferences.Count > 0)
                {
                    // This is a bit tricky. Even though web projects don't use solution configurations,
                    // we want to use the current solution configuration to build the proper configurations
                    // of referenced projects.
                    foreach (SolutionConfigurationInSolution solutionConfiguration in _solutionFile.SolutionConfigurations)
                    {
                        string referenceProjectGuids = null;

                        AddResolveProjectReferenceTasks
                            (
                            traversalProject,
                            newTarget,
                            project,
                            solutionConfiguration,
                            referenceItemName.ToString(),
                            null /* don't care about native references */,
                            out referenceProjectGuids
                            );
                    }
                }

                // Add tasks to capture the auto-refreshed file references (those .REFRESH files).
                AddTasksToResolveAutoRefreshFileReferences(newTarget, project, referenceItemName.ToString());

                // Add a call to RAR (ResolveAssemblyReference) and the Copy task to put the referenced 
                // project outputs in the right place
                AddTasksToCopyAllDependenciesIntoBinDir(newTarget, project, referenceItemName.ToString(), conditionDescribingValidConfigurations.ToString());

                // Add a call to the AspNetCompiler task, conditioned on having a valid Configuration.
                AddTaskForAspNetCompiler(newTarget, project, conditionDescribingValidConfigurations.ToString());

                // Add a call to the <Message> task, conditioned on having an *invalid* Configuration.  The
                // message says that we're skipping the Venus project because it's either not enabled
                // for precompilation, or doesn't support the given configuration.
                ProjectTaskInstance skippingVenusProjectMessageTask = AddErrorWarningMessageInstance
                    (
                    newTarget,
                    "!(" + conditionDescribingValidConfigurations.ToString() + ")",
                    XMakeElements.message,
                    false,
                    "SolutionVenusProjectSkipped"
                    );
            }
        }
        /// <summary>
        /// Adds the targets which build the dependencies and actual project for a metaproject.
        /// </summary>
        private void AddMetaprojectTargetForManagedProject(ProjectInstance traversalProject, ProjectInstance metaprojectInstance, ProjectInSolution project, ProjectConfigurationInSolution projectConfiguration, string targetName, string outputItem)
        {
            string outputItemAsItem = null;
            if (!String.IsNullOrEmpty(outputItem))
            {
                outputItemAsItem = "@(" + outputItem + ")";
            }

            ProjectTargetInstance target = metaprojectInstance.AddTarget(targetName ?? "Build", String.Empty, String.Empty, outputItemAsItem, null, String.Empty, String.Empty, false /* legacy target returns behaviour */);

            AddReferencesBuildTask(metaprojectInstance, target, targetName, outputItem);

            // Add the task to build the actual project.
            AddProjectBuildTask(traversalProject, project, projectConfiguration, target, targetName, EscapingUtilities.Escape(project.AbsolutePath), String.Empty, outputItem);
        }