/// <summary> /// Find all the build products created by compiling the given project file /// </summary> /// <param name="ProjectFile">Initial project file to read. All referenced projects will also be read.</param> /// <param name="InitialProperties">Mapping of property name to value</param> /// <param name="OutBuildProducts">Receives a set of build products on success</param> /// <returns>True if the build products were found, false otherwise.</returns> static bool FindBuildProducts(HashSet <FileReference> ProjectFiles, Dictionary <string, string> InitialProperties, out HashSet <FileReference> OutBuildProducts) { // Read all the project information into a dictionary Dictionary <FileReference, MsBuildProjectInfo> FileToProjectInfo = new Dictionary <FileReference, MsBuildProjectInfo>(); foreach (FileReference ProjectFile in ProjectFiles) { if (!ReadProjectsRecursively(ProjectFile, InitialProperties, FileToProjectInfo)) { OutBuildProducts = null; return(false); } } // Find all the build products HashSet <FileReference> BuildProducts = new HashSet <FileReference>(); foreach (KeyValuePair <FileReference, MsBuildProjectInfo> Pair in FileToProjectInfo) { MsBuildProjectInfo ProjectInfo = Pair.Value; // Add the standard build products DirectoryReference OutputDir = ProjectInfo.GetOutputDir(Pair.Key.Directory); ProjectInfo.AddBuildProducts(OutputDir, BuildProducts); // Add the referenced assemblies foreach (FileReference OtherAssembly in ProjectInfo.References.Where(x => x.Value).Select(x => x.Key)) { FileReference OutputFile = FileReference.Combine(OutputDir, OtherAssembly.GetFileName()); BuildProducts.Add(OutputFile); FileReference SymbolFile = OtherAssembly.ChangeExtension(".pdb"); if (SymbolFile.Exists()) { BuildProducts.Add(OutputFile.ChangeExtension(".pdb")); } } // Add build products from all the referenced projects. MSBuild only copy the directly referenced build products, not recursive references or other assemblies. foreach (MsBuildProjectInfo OtherProjectInfo in ProjectInfo.ProjectReferences.Where(x => x.Value).Select(x => FileToProjectInfo[x.Key])) { OtherProjectInfo.AddBuildProducts(OutputDir, BuildProducts); } } // Update the output set OutBuildProducts = BuildProducts; return(true); }
/// <summary> /// Attempts to read project information for the given file. /// </summary> /// <param name="File">The project file to read</param> /// <param name="Properties">Initial set of property values</param> /// <param name="OutProjectInfo">If successful, the parsed project info</param> /// <returns>True if the project was read successfully, false otherwise</returns> public static bool TryRead(FileReference File, Dictionary <string, string> Properties, out MsBuildProjectInfo OutProjectInfo) { // Read the project file XmlDocument Document = new XmlDocument(); Document.Load(File.FullName); // Check the root element is the right type // HashSet<FileReference> ProjectBuildProducts = new HashSet<FileReference>(); if (Document.DocumentElement.Name != "Project") { OutProjectInfo = null; return(false); } // Parse the basic structure of the document, updating properties and recursing into other referenced projects as we go MsBuildProjectInfo ProjectInfo = new MsBuildProjectInfo(Properties); foreach (XmlElement Element in Document.DocumentElement.ChildNodes.OfType <XmlElement>()) { switch (Element.Name) { case "PropertyGroup": if (EvaluateCondition(Element, ProjectInfo.Properties)) { ParsePropertyGroup(Element, ProjectInfo.Properties); } break; case "ItemGroup": if (EvaluateCondition(Element, ProjectInfo.Properties)) { ParseItemGroup(File.Directory, Element, ProjectInfo); } break; } } // Return the complete project OutProjectInfo = ProjectInfo; return(true); }
/// <summary> /// Read a project file, plus all the project files it references. /// </summary> /// <param name="File">Project file to read</param> /// <param name="InitialProperties">Mapping of property name to value for the initial project</param> /// <param name="FileToProjectInfo"></param> /// <returns>True if the projects were read correctly, false (and prints an error to the log) if not</returns> static bool ReadProjectsRecursively(FileReference File, Dictionary <string, string> InitialProperties, Dictionary <FileReference, MsBuildProjectInfo> FileToProjectInfo) { // Early out if we've already read this project, return succes if (FileToProjectInfo.ContainsKey(File)) { return(true); } // Try to read this project MsBuildProjectInfo ProjectInfo; if (!MsBuildProjectInfo.TryRead(File, InitialProperties, out ProjectInfo)) { CommandUtils.LogError("Couldn't read project '{0}'", File.FullName); return(false); } // Add it to the project lookup, and try to read all the projects it references FileToProjectInfo.Add(File, ProjectInfo); return(ProjectInfo.ProjectReferences.Keys.All(x => ReadProjectsRecursively(x, new Dictionary <string, string>(), FileToProjectInfo))); }
/// <summary> /// Parses an 'ItemGroup' element. /// </summary> /// <param name="BaseDirectory">Base directory to resolve relative paths against</param> /// <param name="ParentElement">The parent 'ItemGroup' element</param> /// <param name="ProjectInfo">Project info object to be updated</param> static void ParseItemGroup(DirectoryReference BaseDirectory, XmlElement ParentElement, MsBuildProjectInfo ProjectInfo) { // Parse any external assembly references foreach (XmlElement ItemElement in ParentElement.ChildNodes.OfType <XmlElement>()) { switch (ItemElement.Name) { case "Reference": // Reference to an external assembly if (EvaluateCondition(ItemElement, ProjectInfo.Properties)) { ParseReference(BaseDirectory, ItemElement, ProjectInfo.References); } break; case "ProjectReference": // Reference to another project if (EvaluateCondition(ItemElement, ProjectInfo.Properties)) { ParseProjectReference(BaseDirectory, ItemElement, ProjectInfo.ProjectReferences); } break; } } }