/// <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); }