| File: ResolveNonMSBuildProjectOutput.cs | Web Access |
| Project: src\msbuild\src\Tasks\Microsoft.Build.Tasks.csproj (Microsoft.Build.Tasks.Core) |
// 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.Reflection; using System.Xml; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; #nullable disable namespace Microsoft.Build.Tasks { /// <remarks> /// This task determines the output files for non-MSBuild project references. We look inside /// a special property set by the VS IDE for the list of project guids and their associated outputs. /// While there's nothing that would prevent resolution of MSBuild projects in this task, the IDE /// only pre-resolves non-MSBuild projects so that we can separate MSBuild project references from /// non-MSBuild ones and return the list of MSBuild projects as UnresolvedProjectReferences. /// Then we can use more powerful MSBuild mechanisms to manipulate just the MSBuild project /// references (i.e. calling into specific targets of references to get the manifest file name) /// which would not be possible with a mixed list of MSBuild and non-MSBuild references. /// </remarks> public class ResolveNonMSBuildProjectOutput : ResolveProjectBase { #region Constructors /// <summary> /// default public constructor /// </summary> public ResolveNonMSBuildProjectOutput() { // do nothing } #endregion #region Properties /// <summary> /// A special XML string containing resolved project outputs - we need to simply match the projects and /// return the appropriate paths /// </summary> public string PreresolvedProjectOutputs { get; set; } /// <summary> /// The list of resolved reference paths (preserving the original project reference attributes) /// </summary> [Output] public ITaskItem[] ResolvedOutputPaths { get; set; } /// <summary> /// The list of project reference items that could not be resolved using the pre-resolved list of outputs. /// Since VS only pre-resolves non-MSBuild projects, this means that project references in this list /// are in the MSBuild format. /// </summary> [Output] public ITaskItem[] UnresolvedProjectReferences { get; set; } /// <summary> /// A delegate with a signature that matches AssemblyName.GetAssemblyName. /// </summary> internal delegate AssemblyName GetAssemblyNameDelegate(string path); /// <summary> /// A dependency-injection way of getting an assembly name. /// </summary> internal GetAssemblyNameDelegate GetAssemblyName { get; set; } #endregion #region ITask Members /// <summary> /// Main task method /// </summary> public override bool Execute() { // Allow unit tests to inject a non-file system dependent version of this. if (GetAssemblyName == null) { GetAssemblyName = AssemblyName.GetAssemblyName; } try { if (!VerifyProjectReferenceItems(ProjectReferences, false /* treat problems as warnings */)) { return false; } var resolvedPaths = new List<ITaskItem>(ProjectReferences.GetLength(0)); var unresolvedReferences = new List<ITaskItem>(ProjectReferences.GetLength(0)); CacheProjectElementsFromXml(PreresolvedProjectOutputs); foreach (ITaskItem projectRef in ProjectReferences) { Log.LogMessageFromResources(MessageImportance.Low, "ResolveNonMSBuildProjectOutput.ProjectReferenceResolutionStarting", projectRef.ItemSpec); bool resolveSuccess = ResolveProject(projectRef, out ITaskItem resolvedPath); if (resolveSuccess) { if (resolvedPath.ItemSpec.Length > 0) { // VC project system does not look like an MSBuild project type yet because VC do not // yet implement IVSProjectBuildSystem. So project references to VC managed libraries // need to be recognized as such. // Even after VC implements this IVSProjectBuildSystem, other project types (NMake, NAnt, etc.) // still can generate managed assemblies, in which case we still need to perform this managed // assembly check. try { GetAssemblyName(resolvedPath.ItemSpec); resolvedPath.SetMetadata("ManagedAssembly", "true"); } catch (BadImageFormatException) { } resolvedPaths.Add(resolvedPath); Log.LogMessageFromResources(MessageImportance.Low, "ResolveNonMSBuildProjectOutput.ProjectReferenceResolutionSuccess", projectRef.ItemSpec, resolvedPath.ItemSpec); } // If resolved path is empty, log a warning. This means that this reference is not an MSBuild reference, // but could not be resolved in the IDE. else { Log.LogWarningWithCodeFromResources("ResolveNonMSBuildProjectOutput.ProjectReferenceResolutionFailure", projectRef.ItemSpec); } } else { unresolvedReferences.Add(projectRef); // This is not an error - we pass unresolved references to UnresolvedProjectReferences for further // processing in the .targets file. Log.LogMessageFromResources(MessageImportance.Low, "ResolveNonMSBuildProjectOutput.ProjectReferenceUnresolved", projectRef.ItemSpec); } } ResolvedOutputPaths = resolvedPaths.ToArray(); UnresolvedProjectReferences = unresolvedReferences.ToArray(); } catch (XmlException e) { Log.LogErrorWithCodeFromResources("General.ErrorExecutingTask", this.GetType().Name, e.Message); return false; } return true; } #endregion #region Methods /// <summary> /// Given a project reference task item and an XML document containing pre-resolved output paths, /// find the output path for that task item. /// </summary> /// <param name="projectRef">project reference to resolve</param> /// <param name="resolvedPath">resulting ITaskItem containing the resolved path</param> /// <returns>true if resolved successfully</returns> internal bool ResolveProject(ITaskItem projectRef, out ITaskItem resolvedPath) { string projectOutputPath = GetProjectItem(projectRef); if (projectOutputPath != null) { resolvedPath = new TaskItem(projectOutputPath); projectRef.CopyMetadataTo(resolvedPath); return true; } resolvedPath = null; return false; } #endregion } }