using Microsoft.Build.Construction; using Microsoft.Build.Exceptions; using System; using System.Collections.Generic; using System.Linq; using Semmle.Util; namespace Semmle.Autobuild { /// /// A solution file, extension .sln. /// public interface ISolution { /// /// List of C# or C++ projects contained in the solution. /// (There could be other project types as well - these are ignored.) /// IEnumerable Projects { get; } /// /// Solution configurations. /// IEnumerable Configurations { get; } /// /// The default configuration name, e.g. "Release" /// string DefaultConfigurationName { get; } /// /// The default platform name, e.g. "x86" /// string DefaultPlatformName { get; } /// /// The path of the solution file. /// string Path { get; } /// /// The number of C# or C++ projects. /// int ProjectCount { get; } /// /// Gets the "best" tools version for this solution. /// If there are several versions, because the project files /// are inconsistent, then pick the highest/latest version. /// If no tools versions are present, return 0.0.0.0. /// Version ToolsVersion { get; } } /// /// A solution file on the filesystem, read using Microsoft.Build. /// class Solution : ISolution { readonly SolutionFile solution; public IEnumerable Projects { get; private set; } public IEnumerable Configurations => solution == null ? Enumerable.Empty() : solution.SolutionConfigurations; public string DefaultConfigurationName => solution == null ? "" : solution.GetDefaultConfigurationName(); public string DefaultPlatformName => solution == null ? "" : solution.GetDefaultPlatformName(); public Solution(Autobuilder builder, string path) { Path = System.IO.Path.GetFullPath(path); try { solution = SolutionFile.Parse(Path); Projects = solution.ProjectsInOrder. Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat). Select(p => System.IO.Path.GetFullPath(FileUtils.ConvertToNative(p.AbsolutePath))). Where(p => builder.Options.Language.ProjectFileHasThisLanguage(p)). Select(p => new Project(builder, p)). ToArray(); } catch (InvalidProjectFileException) { // We allow specifying projects as solutions in lgtm.yml, so model // that scenario as a solution with just that one project Projects = Language.IsProjectFileForAnySupportedLanguage(Path) ? new[] { new Project(builder, Path) } : new Project[0]; } } public string Path { get; private set; } public int ProjectCount => Projects.Count(); IEnumerable ToolsVersions => Projects.Where(p => p.ValidToolsVersion).Select(p => p.ToolsVersion); public Version ToolsVersion => ToolsVersions.Any() ? ToolsVersions.Max() : new Version(); } }