/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Workspaces/MSBuild/Test/SolutionGeneration.cs
372 строки
12 KB
Cyrus Najmabadi
Use raw strings in tests
09 июл 2025, 08:52
09 июл 2025, 08:52
f265dce
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml.Linq; using Microsoft.CodeAnalysis.UnitTests.TestFiles; namespace Microsoft.CodeAnalysis.MSBuild.UnitTests; /// <summary> /// Flexible and extensible API to generate MSBuild projects and solutions without external files or resources. /// </summary> public static class SolutionGeneration { public const string NS = "http://schemas.microsoft.com/developer/msbuild/2003"; private const string CSharpProjectTemplate = """ <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> </PropertyGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </Project> """; private const string SolutionTemplate = """ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.30110.0 MinimumVisualStudioVersion = 10.0.40219.1 {0}Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal """; public const string PublicKey = "00240000048000009400000006020000002400005253413100040000010001003bb5de1b79bee9bf5ba44bdb42974c6f40fdc4b329c8e1b833fa798cf0859529485b2bfc359a08e16f025fe57efd293c4dc3541cb2e0929b1c4a92db87eed7a9454dbd08beb7c7308941384b3bfb088de781b51caef23677f8f6defb671e97e1fc5e0979858e52828c86aca1d4ea1797f1f1254bf64073a28e5be520d5397fb0"; public const string PublicKeyToken = "39d7e8ec38707fde"; public static readonly byte[] KeySnk = Resources.Key_snk; public static IEnumerable<(string fileName, object fileContent)> GetSolutionFiles(params IBuilder[] inputs) { var list = new List<(string, object)>(); var projectBuilders = inputs.OfType<ProjectBuilder>(); var files = new HashSet<string>(StringComparer.OrdinalIgnoreCase); var fileIndex = 1; var projectIndex = 1; // first make sure all projects have names, as a separate loop foreach (var project in projectBuilders) { if (project.Name == null) { project.Name = "Project" + projectIndex; projectIndex++; } } foreach (var project in projectBuilders) { foreach (var document in project.Documents) { if (document.FilePath == null) { document.FilePath = "Document" + fileIndex + (project.Language == LanguageNames.VisualBasic ? ".vb" : ".cs"); fileIndex++; } } foreach (var projectReference in project.ProjectReferences) { if (projectReference.Guid == Guid.Empty) { var referencedProject = projectBuilders.First(p => p.Name == projectReference.ProjectName); projectReference.Guid = referencedProject.Guid; projectReference.ProjectFileName = referencedProject.Name + referencedProject.Extension; } } foreach (var (fileName, fileContent) in project.Files) { if (files.Add(fileName + fileContent)) { list.Add((fileName, fileContent)); } } } list.Add(("Solution.sln", GetSolutionContent(projectBuilders))); return list; } public static IBuilder Project(params IBuilder[] inputs) { var projectReferences = inputs.OfType<ProjectReferenceBuilder>(); var documents = inputs.OfType<DocumentBuilder>().ToList(); var projectName = inputs.OfType<ProjectNameBuilder>().FirstOrDefault(); var properties = inputs.OfType<PropertyBuilder>().ToList(); var sign = inputs.OfType<SignBuilder>(); if (sign != null) { properties.Add((PropertyBuilder)Property("SignAssembly", "true")); properties.Add((PropertyBuilder)Property("AssemblyOriginatorKeyFile", "key.snk")); documents.Add((DocumentBuilder)Document(KeySnk, "key.snk", "None")); } return new ProjectBuilder { Name = projectName?.Name, Documents = documents, ProjectReferences = projectReferences, Properties = properties }; } public static IBuilder ProjectReference(string projectName) { return new ProjectReferenceBuilder { ProjectName = projectName }; } public static IBuilder ProjectName(string projectName) { return new ProjectNameBuilder { Name = projectName }; } public static IBuilder Property(string propertyName, string propertyValue) { return new PropertyBuilder { Name = propertyName, Value = propertyValue }; } public static IBuilder Sign { get { return new SignBuilder(); } } public static IBuilder Document( object content = null, string filePath = null, string itemType = "Compile") { return new DocumentBuilder { FilePath = filePath, Content = content, ItemType = itemType }; } private static string GetSolutionContent(IEnumerable<ProjectBuilder> projects) { var sb = new StringBuilder(); foreach (var project in projects) { var fileName = project.Name + project.Extension; var languageGuid = project.Language == LanguageNames.VisualBasic ? "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}" : "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"; sb.AppendLine( string.Format( """ Project("{0}") = "{1}", "{2}", "{3}" """, languageGuid, project.Name, fileName, project.Guid.ToString("B"))); sb.AppendLine("EndProject"); } return string.Format(SolutionTemplate, sb.ToString()); } public interface IBuilder { } private sealed class ProjectBuilder : IBuilder { public string Name { get; set; } public string Language { get; set; } public Guid Guid { get; set; } public string OutputType { get; set; } public string OutputPath { get; set; } public IEnumerable<DocumentBuilder> Documents { get; set; } public IEnumerable<ProjectReferenceBuilder> ProjectReferences { get; set; } public IEnumerable<PropertyBuilder> Properties { get; set; } public string Extension { get { return Language == LanguageNames.VisualBasic ? ".vbproj" : ".csproj"; } } public IEnumerable<(string fileName, object fileContent)> Files { get { foreach (var document in Documents) { yield return (document.FilePath, document.Content); } yield return (Name + Extension, GetProjectContent()); } } private string GetProjectContent() { if (Language == LanguageNames.VisualBasic) { throw new NotImplementedException("Need VB support"); } if (Guid == Guid.Empty) { Guid = Guid.NewGuid(); } if (string.IsNullOrEmpty(OutputType)) { OutputType = "Library"; } if (string.IsNullOrEmpty(OutputPath)) { OutputPath = "."; } var document = XDocument.Parse(CSharpProjectTemplate); var propertyGroup = document.Root.Descendants(XName.Get("PropertyGroup", NS)).First(); AddXElement(propertyGroup, "ProjectGuid", Guid.ToString("B")); AddXElement(propertyGroup, "OutputType", OutputType); AddXElement(propertyGroup, "OutputPath", OutputPath); AddXElement(propertyGroup, "AssemblyName", Name); if (Properties != null) { foreach (var property in Properties) { AddXElement(propertyGroup, property.Name, property.Value); } } var importTargets = document.Root.Elements().Last(); if (ProjectReferences != null && ProjectReferences.Any()) { AddItemGroup( importTargets, _ => "ProjectReference", ProjectReferences, i => i.ProjectFileName, (projectReference, xmlElement) => { if (projectReference.Guid != Guid.Empty) { AddXElement(xmlElement, "Project", projectReference.Guid.ToString("B")); } AddXElement(xmlElement, "Name", Path.GetFileNameWithoutExtension(projectReference.ProjectName)); }); } if (Documents != null) { AddItemGroup( importTargets, i => i.ItemType, Documents, i => i.FilePath); } return document.ToString(); } private static void AddItemGroup<T>( XElement addBefore, Func<T, string> itemTypeSelector, IEnumerable<T> items, Func<T, string> attributeValueGetter, Action<T, XElement> elementModifier = null) { var itemGroup = CreateXElement("ItemGroup"); addBefore.AddBeforeSelf(itemGroup); foreach (var item in items) { var itemElement = CreateXElement(itemTypeSelector(item)); itemElement.SetAttributeValue("Include", attributeValueGetter(item)); elementModifier?.Invoke(item, itemElement); itemGroup.Add(itemElement); } } private static XElement CreateXElement(string name) { return new XElement(XName.Get(name, NS)); } private static void AddXElement(XElement element, string elementName, string elementValue) { element.Add(new XElement(XName.Get(elementName, NS), elementValue)); } } private sealed class ProjectReferenceBuilder : IBuilder { public string ProjectName { get; set; } public Guid Guid { get; set; } public string ProjectFileName { get; set; } } private sealed class ProjectNameBuilder : IBuilder { public string Name { get; set; } } private sealed class PropertyBuilder : IBuilder { public string Name { get; set; } public string Value { get; set; } } private sealed class SignBuilder : IBuilder { } private sealed class DocumentBuilder : IBuilder { public string FilePath { get; set; } public object Content { get; set; } public string ItemType { get; set; } } }