using Microsoft.Build.Construction;
using Microsoft.Build.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using Semmle.Util;
using System.IO;
using Semmle.Util.Logging;
namespace Semmle.Autobuild
{
///
/// A solution file, extension .sln.
///
public interface ISolution : IProjectOrSolution
{
///
/// 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; }
///
/// 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 : ProjectOrSolution, ISolution
{
readonly SolutionFile solution;
readonly IEnumerable includedProjects;
public override IEnumerable IncludedProjects => includedProjects;
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, bool allowProject) : base(builder, path)
{
try
{
solution = SolutionFile.Parse(FullPath);
}
catch (Exception e) when (e is InvalidProjectFileException || e is FileNotFoundException)
{
// We allow specifying projects as solutions in lgtm.yml, so model
// that scenario as a solution with just that one project
if (allowProject)
{
includedProjects = new[] { new Project(builder, path) };
return;
}
builder.Log(Severity.Info, $"Unable to read solution file {path}.");
includedProjects = new Project[0];
return;
}
includedProjects =
solution.ProjectsInOrder.
Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat).
Select(p => builder.Actions.PathCombine(Path.GetDirectoryName(path), builder.Actions.PathCombine(p.RelativePath.Split('\\', StringSplitOptions.RemoveEmptyEntries)))).
Select(p => new Project(builder, p)).
ToArray();
}
IEnumerable ToolsVersions => includedProjects.Where(p => p.ValidToolsVersion).Select(p => p.ToolsVersion);
public Version ToolsVersion => ToolsVersions.Any() ? ToolsVersions.Max() : new Version();
}
}