/// <summary> /// Adds an MSBuild task to the specified target /// </summary> private static ProjectTaskInstance AddMSBuildTaskInstance ( ProjectTargetInstance target, string projectPath, string msbuildTargetName, string configurationName, string platformName, bool specifyProjectToolsVersion ) { ProjectTaskInstance msbuildTask = target.AddTask("MSBuild", null, null); msbuildTask.SetParameter("Projects", EscapingUtilities.Escape(projectPath)); if (msbuildTargetName != null && msbuildTargetName.Length > 0) { msbuildTask.SetParameter("Targets", msbuildTargetName); } string additionalProperties = string.Format( CultureInfo.InvariantCulture, "Configuration={0}; Platform={1}; BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)", EscapingUtilities.Escape(configurationName), EscapingUtilities.Escape(platformName) ); msbuildTask.SetParameter("Properties", additionalProperties); if (specifyProjectToolsVersion) { msbuildTask.SetParameter("ToolsVersion", "$(ProjectToolsVersion)"); } return msbuildTask; }
/// <summary> /// Add a new error/warning/message tag into the given target /// </summary> private static ProjectTaskInstance AddErrorWarningMessageInstance ( ProjectTargetInstance target, string condition, string elementType, bool treatAsLiteral, string textResourceName, params object[] args ) { string code = null; string helpKeyword = null; string text = ResourceUtilities.FormatResourceString(out code, out helpKeyword, textResourceName, args); if (treatAsLiteral) { text = EscapingUtilities.Escape(text); } ProjectTaskInstance task = target.AddTask(elementType, condition, null); task.SetParameter("Text", text); if ((elementType != XMakeElements.message) && (code != null)) { task.SetParameter("Code", EscapingUtilities.Escape(code)); } if ((elementType != XMakeElements.message) && (helpKeyword != null)) { task.SetParameter("HelpKeyword", EscapingUtilities.Escape(helpKeyword)); } return task; }
/// <summary> /// Add a call to the ResolveAssemblyReference task to crack the pre-resolved referenced /// assemblies for the complete list of dependencies, PDBs, satellites, etc. The invoke /// the Copy task to copy all these files (or at least the ones that RAR determined should /// be copied local) into the web project's bin directory. /// </summary> private static void AddTasksToCopyAllDependenciesIntoBinDir ( ProjectTargetInstance target, ProjectInSolution project, string referenceItemName, string conditionDescribingValidConfigurations ) { string copyLocalFilesItemName = referenceItemName + "_CopyLocalFiles"; string targetFrameworkDirectoriesName = GenerateSafePropertyName(project, "_TargetFrameworkDirectories"); string fullFrameworkRefAssyPathName = GenerateSafePropertyName(project, "_FullFrameworkReferenceAssemblyPaths"); string destinationFolder = String.Format(CultureInfo.InvariantCulture, @"$({0})\Bin\", GenerateSafePropertyName(project, "AspNetPhysicalPath")); // This is a bit of a hack. We're actually calling the "Copy" task on all of // the *non-existent* files. Why? Because we want to emit a warning in the // log for each non-existent file, and the Copy task does that nicely for us. // I would have used the <Warning> task except for the fact that we are in // string-resource lockdown. ProjectTaskInstance copyNonExistentReferencesTask = target.AddTask("Copy", String.Format(CultureInfo.InvariantCulture, "!Exists('%({0}.Identity)')", referenceItemName), "true"); copyNonExistentReferencesTask.SetParameter("SourceFiles", "@(" + referenceItemName + "->'%(FullPath)')"); copyNonExistentReferencesTask.SetParameter("DestinationFolder", destinationFolder); // We need to determine the appropriate TargetFrameworkMoniker to pass to GetReferenceAssemblyPaths, // so that we will pass the appropriate target framework directories to RAR. ProjectTaskInstance getRefAssembliesTask = target.AddTask("GetReferenceAssemblyPaths", null, null); getRefAssembliesTask.SetParameter("TargetFrameworkMoniker", project.TargetFrameworkMoniker); getRefAssembliesTask.SetParameter("RootPath", "$(TargetFrameworkRootPath)"); getRefAssembliesTask.AddOutputProperty("ReferenceAssemblyPaths", targetFrameworkDirectoriesName, null); getRefAssembliesTask.AddOutputProperty("FullFrameworkReferenceAssemblyPaths", fullFrameworkRefAssyPathName, null); // Call ResolveAssemblyReference on each of the .DLL files that were found on // disk from the .REFRESH files as well as the P2P references. RAR will crack // the dependencies, find PDBs, satellite assemblies, etc., and determine which // files need to be copy-localed. ProjectTaskInstance rarTask = target.AddTask("ResolveAssemblyReference", String.Format(CultureInfo.InvariantCulture, "Exists('%({0}.Identity)')", referenceItemName), null); rarTask.SetParameter("Assemblies", "@(" + referenceItemName + "->'%(FullPath)')"); rarTask.SetParameter("TargetFrameworkDirectories", "$(" + targetFrameworkDirectoriesName + ")"); rarTask.SetParameter("FullFrameworkFolders", "$(" + fullFrameworkRefAssyPathName + ")"); rarTask.SetParameter("SearchPaths", "{RawFileName};{TargetFrameworkDirectory};{GAC}"); rarTask.SetParameter("FindDependencies", "true"); rarTask.SetParameter("FindSatellites", "true"); rarTask.SetParameter("FindSerializationAssemblies", "true"); rarTask.SetParameter("FindRelatedFiles", "true"); rarTask.SetParameter("TargetFrameworkMoniker", project.TargetFrameworkMoniker); rarTask.AddOutputItem("CopyLocalFiles", copyLocalFilesItemName, null); // Copy all the copy-local files (reported by RAR) to the web project's "bin" // directory. ProjectTaskInstance copyTask = target.AddTask("Copy", conditionDescribingValidConfigurations, null); copyTask.SetParameter("SourceFiles", "@(" + copyLocalFilesItemName + ")"); copyTask.SetParameter ( "DestinationFiles", String.Format(CultureInfo.InvariantCulture, @"@({0}->'{1}%(DestinationSubDirectory)%(Filename)%(Extension)')", copyLocalFilesItemName, destinationFolder) ); }
/// <summary> /// This code handles the *.REFRESH files that are in the "bin" subdirectory of /// a web project. These .REFRESH files are just text files that contain absolute or /// relative paths to the referenced assemblies. The goal of these tasks is to /// search all *.REFRESH files and extract fully-qualified absolute paths for /// each of the references. /// </summary> private static void AddTasksToResolveAutoRefreshFileReferences ( ProjectTargetInstance target, ProjectInSolution project, string referenceItemName ) { string webRoot = "$(" + GenerateSafePropertyName(project, "AspNetPhysicalPath") + ")"; // Create an item list containing each of the .REFRESH files. ProjectTaskInstance createItemTask = target.AddTask("CreateItem", null, null); createItemTask.SetParameter("Include", webRoot + @"\Bin\*.refresh"); createItemTask.AddOutputItem("Include", referenceItemName + "_RefreshFile", null); // Read the lines out of each .REFRESH file; they should be paths to .DLLs. Put these paths // into an item list. ProjectTaskInstance readLinesTask = target.AddTask("ReadLinesFromFile", String.Format(CultureInfo.InvariantCulture, @" '%({0}_RefreshFile.Identity)' != '' ", referenceItemName), null); readLinesTask.SetParameter("File", String.Format(CultureInfo.InvariantCulture, @"%({0}_RefreshFile.Identity)", referenceItemName)); readLinesTask.AddOutputItem("Lines", referenceItemName + "_ReferenceRelPath", null); // Take those paths and combine them with the root of the web project to form either // an absolute path or a path relative to the .SLN file. These paths can be passed // directly to RAR later. ProjectTaskInstance combinePathTask = target.AddTask("CombinePath", null, null); combinePathTask.SetParameter("BasePath", webRoot); combinePathTask.SetParameter("Paths", String.Format(CultureInfo.InvariantCulture, @"@({0}_ReferenceRelPath)", referenceItemName)); combinePathTask.AddOutputItem("CombinedPaths", referenceItemName, null); }
/// <summary> /// Adds a task which builds the @(ProjectReference) items. /// </summary> private void AddReferencesBuildTask(ProjectInstance projectInstance, ProjectTargetInstance target, string targetToBuild, string outputItem) { ProjectTaskInstance task = target.AddTask("MSBuild", String.Empty, String.Empty); if (String.Equals(targetToBuild, "Clean", StringComparison.OrdinalIgnoreCase)) { task.SetParameter("Projects", "@(ProjectReference->Reverse())"); } else { task.SetParameter("Projects", "@(ProjectReference)"); // The references already have the tools versions and properties set on them. } if (targetToBuild != null) { task.SetParameter("Targets", targetToBuild); } task.SetParameter("BuildInParallel", "True"); task.SetParameter("Properties", SolutionProperties); // We only want to build "nonexistent" projects if we're building metaprojects, since they don't exist on disk. Otherwise, // we still want to error when the referenced project doesn't exist. task.SetParameter("SkipNonexistentProjects", "%(ProjectReference.SkipNonexistentProjects)"); if (outputItem != null) { task.AddOutputItem("TargetOutputs", outputItem, String.Empty); } }
/// <summary> /// Adds MSBuild tasks to a project target to pre-resolve its project references /// </summary> private void AddResolveProjectReferenceTasks ( ProjectInstance traversalProject, ProjectTargetInstance target, ProjectInSolution project, SolutionConfigurationInSolution solutionConfiguration, string outputReferenceItemName, string outputImportLibraryItemName, out string addedReferenceGuids ) { StringBuilder referenceGuids = new StringBuilder(); string message = null; // Suffix for the reference item name. Since we need to attach additional (different) metadata to every // reference item, we need to have helper item lists each with only one item int outputReferenceItemNameSuffix = 0; // Pre-resolve the MSBuild project references foreach (string projectReferenceGuid in project.ProjectReferences) { ProjectInSolution referencedProject = (ProjectInSolution)_solutionFile.ProjectsByGuid[projectReferenceGuid]; ProjectConfigurationInSolution referencedProjectConfiguration = null; if ((referencedProject != null) && (referencedProject.ProjectConfigurations.TryGetValue(solutionConfiguration.FullName, out referencedProjectConfiguration)) && (referencedProjectConfiguration != null)) { string outputReferenceItemNameWithSuffix = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", outputReferenceItemName, outputReferenceItemNameSuffix); if ((referencedProject.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat) || ((referencedProject.ProjectType == SolutionProjectType.Unknown) && (referencedProject.CanBeMSBuildProjectFile(out message)))) { string condition = GetConditionStringForConfiguration(solutionConfiguration); if (traversalProject.EvaluateCondition(condition)) { bool specifyProjectToolsVersion = String.Equals(traversalProject.ToolsVersion, "2.0", StringComparison.OrdinalIgnoreCase) ? false : true; ProjectTaskInstance msbuildTask = AddMSBuildTaskInstance ( target, referencedProject.RelativePath, "GetTargetPath", referencedProjectConfiguration.ConfigurationName, referencedProjectConfiguration.PlatformName, specifyProjectToolsVersion ); msbuildTask.AddOutputItem("TargetOutputs", outputReferenceItemNameWithSuffix, null); } if (referenceGuids.Length > 0) { referenceGuids.Append(';'); } referenceGuids.Append(projectReferenceGuid); // This merges the one-item item list into the main list, adding the appropriate guid metadata ProjectTaskInstance createItemTask = target.AddTask("CreateItem", null, null); createItemTask.SetParameter("Include", "@(" + outputReferenceItemNameWithSuffix + ")"); createItemTask.SetParameter("AdditionalMetadata", "Guid=" + projectReferenceGuid); createItemTask.AddOutputItem("Include", outputReferenceItemName, null); } outputReferenceItemNameSuffix++; } } addedReferenceGuids = referenceGuids.ToString(); }
/// <summary> /// Helper method to add a call to the AspNetCompiler task into the given target. /// </summary> private void AddTaskForAspNetCompiler ( ProjectTargetInstance target, ProjectInSolution project, string conditionDescribingValidConfigurations ) { // Add a call to the AspNetCompiler task, conditioned on having a valid Configuration. ProjectTaskInstance newTask = target.AddTask("AspNetCompiler", conditionDescribingValidConfigurations, null); newTask.SetParameter("VirtualPath", "$(" + GenerateSafePropertyName(project, "AspNetVirtualPath") + ")"); newTask.SetParameter("PhysicalPath", "$(" + GenerateSafePropertyName(project, "AspNetPhysicalPath") + ")"); newTask.SetParameter("TargetPath", "$(" + GenerateSafePropertyName(project, "AspNetTargetPath") + ")"); newTask.SetParameter("Force", "$(" + GenerateSafePropertyName(project, "AspNetForce") + ")"); newTask.SetParameter("Updateable", "$(" + GenerateSafePropertyName(project, "AspNetUpdateable") + ")"); newTask.SetParameter("Debug", "$(" + GenerateSafePropertyName(project, "AspNetDebug") + ")"); newTask.SetParameter("KeyFile", "$(" + GenerateSafePropertyName(project, "AspNetKeyFile") + ")"); newTask.SetParameter("KeyContainer", "$(" + GenerateSafePropertyName(project, "AspNetKeyContainer") + ")"); newTask.SetParameter("DelaySign", "$(" + GenerateSafePropertyName(project, "AspNetDelaySign") + ")"); newTask.SetParameter("AllowPartiallyTrustedCallers", "$(" + GenerateSafePropertyName(project, "AspNetAPTCA") + ")"); newTask.SetParameter("FixedNames", "$(" + GenerateSafePropertyName(project, "AspNetFixedNames") + ")"); bool isDotNetFramework = false; // generate the target .NET Framework version based on the passed in TargetFrameworkMoniker. try { FrameworkName targetFramework = new FrameworkName(project.TargetFrameworkMoniker); if (String.Equals(targetFramework.Identifier, ".NETFramework", StringComparison.OrdinalIgnoreCase)) { isDotNetFramework = true; // As of .NET Framework 4.0, there are only two versions of aspnet_compiler.exe: 2.0 and 4.0. If // the TargetFrameworkVersion is less than 4.0, use the 2.0 version. Otherwise, just use the 4.0 // version of the executable, so that if say FV 4.1 is passed in, we don't throw an error. if (targetFramework.Version.Major >= 4) { newTask.SetParameter ( "ToolPath", FrameworkLocationHelper.GetPathToDotNetFramework(_version40) ); if (targetFramework.Version > _version40) { _loggingService.LogComment(_projectBuildEventContext, MessageImportance.Low, "AspNetCompiler.TargetingHigherFrameworksDefaultsTo40", project.ProjectName, targetFramework.Version.ToString()); } } else { string pathTo20 = FrameworkLocationHelper.GetPathToDotNetFramework(_version20); ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(pathTo20 != null, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(_solutionFile.FullPath), "AspNetCompiler.20NotInstalled"); newTask.SetParameter ( "ToolPath", pathTo20 ); } } } catch (Exception e) { if (ExceptionHandling.NotExpectedException(e)) { throw; } else { ProjectFileErrorUtilities.ThrowInvalidProjectFile ( new BuildEventFileInfo(_solutionFile.FullPath), e, "AspNetCompiler.InvalidTargetFrameworkMonikerFromException", project.ProjectName, project.TargetFrameworkMoniker, e.Message ); } } if (!isDotNetFramework) { ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile ( false, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(_solutionFile.FullPath), "AspNetCompiler.InvalidTargetFrameworkMonikerNotDotNET", project.ProjectName, project.TargetFrameworkMoniker ); } }
/// <summary> /// Adds an MSBuild task to a single metaproject. This is used in the traversal project. /// </summary> private void AddMetaprojectBuildTask(ProjectInstance traversalProject, ProjectInSolution project, ProjectTargetInstance target, string targetToBuild, string outputItem) { ProjectTaskInstance task = target.AddTask("MSBuild", OpportunisticIntern.InternStringIfPossible("'%(ProjectReference.Identity)' == '" + GetMetaprojectName(project) + "'"), String.Empty); task.SetParameter("Projects", "@(ProjectReference)"); if (targetToBuild != null) { task.SetParameter("Targets", targetToBuild); } task.SetParameter("BuildInParallel", "True"); task.SetParameter("ToolsVersion", MSBuildConstants.CurrentToolsVersion); task.SetParameter("Properties", SolutionProperties); task.SetParameter("SkipNonexistentProjects", "%(ProjectReference.SkipNonexistentProjects)"); if (outputItem != null) { task.AddOutputItem("TargetOutputs", outputItem, String.Empty); } }
/// <summary> /// Adds an MSBuild task to a real project. /// </summary> private void AddProjectBuildTask(ProjectInstance traversalProject, ProjectInSolution project, ProjectConfigurationInSolution projectConfiguration, ProjectTargetInstance target, string targetToBuild, string sourceItems, string condition, string outputItem) { ProjectTaskInstance task = target.AddTask("MSBuild", condition, String.Empty); task.SetParameter("Projects", sourceItems); if (targetToBuild != null) { task.SetParameter("Targets", targetToBuild); } task.SetParameter("BuildInParallel", "True"); task.SetParameter("ToolsVersion", GetToolsVersionAttributeForDirectMSBuildTask(traversalProject)); task.SetParameter("Properties", GetPropertiesAttributeForDirectMSBuildTask(projectConfiguration)); if (outputItem != null) { task.AddOutputItem("TargetOutputs", outputItem, String.Empty); } }