using Microsoft.Build.Construction; using System.Collections.Generic; using System.IO; using System.Linq; namespace Semmle.BuildAnalyser { /// /// Access data in a .sln file. /// internal class SolutionFile { private readonly Microsoft.Build.Construction.SolutionFile solutionFile; private string FullPath { get; } /// /// Read the file. /// /// The filename of the .sln. public SolutionFile(string filename) { // SolutionFile.Parse() expects a rooted path. FullPath = Path.GetFullPath(filename); solutionFile = Microsoft.Build.Construction.SolutionFile.Parse(FullPath); } /// /// Projects directly included in the .sln file. /// public IEnumerable MsBuildProjects { get { return solutionFile.ProjectsInOrder .Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat) .Select(p => p.AbsolutePath) .Select(p => Path.DirectorySeparatorChar == '/' ? p.Replace("\\", "/") : p); } } /// /// Projects included transitively via a subdirectory. /// public IEnumerable NestedProjects { get { return solutionFile.ProjectsInOrder .Where(p => p.ProjectType == SolutionProjectType.SolutionFolder) .Where(p => Directory.Exists(p.AbsolutePath)) .SelectMany(p => new DirectoryInfo(p.AbsolutePath).EnumerateFiles("*.csproj", SearchOption.AllDirectories)) .Select(f => f.FullName); } } /// /// List of projects which were mentioned but don't exist on disk. /// public IEnumerable MissingProjects { get { // Only projects in the solution file can be missing. // (NestedProjects are located on disk so always exist.) return MsBuildProjects.Where(p => !File.Exists(p)); } } /// /// The list of project files. /// public IEnumerable Projects => MsBuildProjects.Concat(NestedProjects); } }