mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
261 lines
11 KiB
C#
261 lines
11 KiB
C#
|
|
using Microsoft.Build.Construction;
|
|
using Microsoft.Build.Evaluation;
|
|
using Microsoft.Build.Exceptions;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Collections.Immutable;
|
|
|
|
namespace RoslynWS
|
|
{
|
|
/// <summary>
|
|
/// Helper methods for working with MSBuild projects.
|
|
/// </summary>
|
|
public static class MSBuildHelper
|
|
{
|
|
/// <summary>
|
|
/// The names of well-known item metadata.
|
|
/// </summary>
|
|
public static readonly ImmutableSortedSet<string> WellknownMetadataNames =
|
|
ImmutableSortedSet.Create(
|
|
"FullPath",
|
|
"RootDir",
|
|
"Filename",
|
|
"Extension",
|
|
"RelativeDir",
|
|
"Directory",
|
|
"RecursiveDir",
|
|
"Identity",
|
|
"ModifiedTime",
|
|
"CreatedTime",
|
|
"AccessedTime"
|
|
);
|
|
|
|
/// <summary>
|
|
/// Create an MSBuild project collection.
|
|
/// </summary>
|
|
/// <param name="solutionDirectory">
|
|
/// The base (i.e. solution) directory.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The project collection.
|
|
/// </returns>
|
|
public static ProjectCollection CreateProjectCollection(string solutionDirectory)
|
|
{
|
|
return CreateProjectCollection(solutionDirectory,
|
|
DotNetRuntimeInfo.GetCurrent(solutionDirectory)
|
|
);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an MSBuild project collection.
|
|
/// </summary>
|
|
/// <param name="solutionDirectory">
|
|
/// The base (i.e. solution) directory.
|
|
/// </param>
|
|
/// <param name="runtimeInfo">
|
|
/// Information about the current .NET Core runtime.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The project collection.
|
|
/// </returns>
|
|
public static ProjectCollection CreateProjectCollection(string solutionDirectory, DotNetRuntimeInfo runtimeInfo)
|
|
{
|
|
if (String.IsNullOrWhiteSpace(solutionDirectory))
|
|
throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'baseDir'.", nameof(solutionDirectory));
|
|
|
|
if (runtimeInfo == null)
|
|
throw new ArgumentNullException(nameof(runtimeInfo));
|
|
|
|
if (String.IsNullOrWhiteSpace(runtimeInfo.BaseDirectory))
|
|
throw new InvalidOperationException("Cannot determine base directory for .NET Core.");
|
|
|
|
Dictionary<string, string> globalProperties = CreateGlobalMSBuildProperties(runtimeInfo, solutionDirectory);
|
|
EnsureMSBuildEnvironment(globalProperties);
|
|
|
|
ProjectCollection projectCollection = new ProjectCollection(globalProperties) { IsBuildEnabled = false };
|
|
|
|
// Override toolset paths (for some reason these point to the main directory where the dotnet executable lives).
|
|
Toolset toolset = projectCollection.GetToolset("15.0");
|
|
toolset = new Toolset(
|
|
toolsVersion: "15.0",
|
|
toolsPath: globalProperties["MSBuildExtensionsPath"],
|
|
projectCollection: projectCollection,
|
|
msbuildOverrideTasksPath: ""
|
|
);
|
|
projectCollection.AddToolset(toolset);
|
|
|
|
return projectCollection;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create global properties for MSBuild.
|
|
/// </summary>
|
|
/// <param name="runtimeInfo">
|
|
/// Information about the current .NET Core runtime.
|
|
/// </param>
|
|
/// <param name="solutionDirectory">
|
|
/// The base (i.e. solution) directory.
|
|
/// </param>
|
|
/// <returns>
|
|
/// A dictionary containing the global properties.
|
|
/// </returns>
|
|
public static Dictionary<string, string> CreateGlobalMSBuildProperties(DotNetRuntimeInfo runtimeInfo, string solutionDirectory)
|
|
{
|
|
if (runtimeInfo == null)
|
|
throw new ArgumentNullException(nameof(runtimeInfo));
|
|
|
|
if (String.IsNullOrWhiteSpace(solutionDirectory))
|
|
throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'solutionDirectory'.", nameof(solutionDirectory));
|
|
|
|
if (solutionDirectory.Length > 0 && solutionDirectory[solutionDirectory.Length - 1] != Path.DirectorySeparatorChar)
|
|
solutionDirectory += Path.DirectorySeparatorChar;
|
|
|
|
return new Dictionary<string, string>
|
|
{
|
|
[WellKnownPropertyNames.DesignTimeBuild] = "true",
|
|
[WellKnownPropertyNames.BuildProjectReferences] = "false",
|
|
[WellKnownPropertyNames.ResolveReferenceDependencies] = "true",
|
|
[WellKnownPropertyNames.SolutionDir] = solutionDirectory,
|
|
[WellKnownPropertyNames.MSBuildExtensionsPath] = runtimeInfo.BaseDirectory,
|
|
[WellKnownPropertyNames.MSBuildSDKsPath] = Path.Combine(runtimeInfo.BaseDirectory, "Sdks"),
|
|
[WellKnownPropertyNames.RoslynTargetsPath] = Path.Combine(runtimeInfo.BaseDirectory, "Roslyn")
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensure that environment variables are populated using the specified MSBuild global properties.
|
|
/// </summary>
|
|
/// <param name="globalMSBuildProperties">
|
|
/// The MSBuild global properties
|
|
/// </param>
|
|
public static void EnsureMSBuildEnvironment(Dictionary<string, string> globalMSBuildProperties)
|
|
{
|
|
if (globalMSBuildProperties == null)
|
|
throw new ArgumentNullException(nameof(globalMSBuildProperties));
|
|
|
|
// Kinda sucks that the simplest way to get MSBuild to resolve SDKs correctly is using environment variables, but there you go.
|
|
Environment.SetEnvironmentVariable(
|
|
WellKnownPropertyNames.MSBuildExtensionsPath,
|
|
globalMSBuildProperties[WellKnownPropertyNames.MSBuildExtensionsPath]
|
|
);
|
|
Environment.SetEnvironmentVariable(
|
|
WellKnownPropertyNames.MSBuildSDKsPath,
|
|
globalMSBuildProperties[WellKnownPropertyNames.MSBuildSDKsPath]
|
|
);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Does the specified property name represent a private property?
|
|
/// </summary>
|
|
/// <param name="propertyName">
|
|
/// The property name.
|
|
/// </param>
|
|
/// <returns>
|
|
/// <c>true</c>, if the property name starts with an underscore; otherwise, <c>false</c>.
|
|
/// </returns>
|
|
public static bool IsPrivateProperty(string propertyName) => propertyName?.StartsWith("_") ?? false;
|
|
|
|
/// <summary>
|
|
/// Does the specified metadata name represent a private property?
|
|
/// </summary>
|
|
/// <param name="metadataName">
|
|
/// The metadata name.
|
|
/// </param>
|
|
/// <returns>
|
|
/// <c>true</c>, if the metadata name starts with an underscore; otherwise, <c>false</c>.
|
|
/// </returns>
|
|
public static bool IsPrivateMetadata(string metadataName) => metadataName?.StartsWith("_") ?? false;
|
|
|
|
/// <summary>
|
|
/// Does the specified item type represent a private property?
|
|
/// </summary>
|
|
/// <param name="itemType">
|
|
/// The item type.
|
|
/// </param>
|
|
/// <returns>
|
|
/// <c>true</c>, if the item type starts with an underscore; otherwise, <c>false</c>.
|
|
/// </returns>
|
|
public static bool IsPrivateItemType(string itemType) => itemType?.StartsWith("_") ?? false;
|
|
|
|
/// <summary>
|
|
/// Determine whether the specified metadata name represents well-known (built-in) item metadata.
|
|
/// </summary>
|
|
/// <param name="metadataName">
|
|
/// The metadata name.
|
|
/// </param>
|
|
/// <returns>
|
|
/// <c>true</c>, if <paramref name="metadataName"/> represents well-known item metadata; otherwise, <c>false</c>.
|
|
/// </returns>
|
|
public static bool IsWellKnownItemMetadata(string metadataName) => WellknownMetadataNames.Contains(metadataName);
|
|
|
|
/// <summary>
|
|
/// Create a copy of the project for caching.
|
|
/// </summary>
|
|
/// <param name="project">
|
|
/// The MSBuild project.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The project copy (independent of original, but sharing the same <see cref="ProjectCollection"/>).
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// You can only create a single cached copy for a given project.
|
|
/// </remarks>
|
|
public static Project CloneAsCachedProject(this Project project)
|
|
{
|
|
if (project == null)
|
|
throw new ArgumentNullException(nameof(project));
|
|
|
|
ProjectRootElement clonedXml = project.Xml.DeepClone();
|
|
Project clonedProject = new Project(clonedXml, project.GlobalProperties, project.ToolsVersion, project.ProjectCollection);
|
|
clonedProject.FullPath = Path.ChangeExtension(project.FullPath,
|
|
".cached" + Path.GetExtension(project.FullPath)
|
|
);
|
|
|
|
return clonedProject;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The names of well-known MSBuild properties.
|
|
/// </summary>
|
|
public static class WellKnownPropertyNames
|
|
{
|
|
/// <summary>
|
|
/// The "MSBuildExtensionsPath" property.
|
|
/// </summary>
|
|
public static readonly string MSBuildExtensionsPath = "MSBuildExtensionsPath";
|
|
|
|
/// <summary>
|
|
/// The "MSBuildSDKsPath" property.
|
|
/// </summary>
|
|
public static readonly string MSBuildSDKsPath = "MSBuildSDKsPath";
|
|
|
|
/// <summary>
|
|
/// The "SolutionDir" property.
|
|
/// </summary>
|
|
public static readonly string SolutionDir = "SolutionDir";
|
|
|
|
/// <summary>
|
|
/// The "_ResolveReferenceDependencies" property.
|
|
/// </summary>
|
|
public static readonly string ResolveReferenceDependencies = "_ResolveReferenceDependencies";
|
|
|
|
/// <summary>
|
|
/// The "DesignTimeBuild" property.
|
|
/// </summary>
|
|
public static readonly string DesignTimeBuild = "DesignTimeBuild";
|
|
|
|
/// <summary>
|
|
/// The "BuildProjectReferences" property.
|
|
/// </summary>
|
|
public static readonly string BuildProjectReferences = "BuildProjectReferences";
|
|
|
|
/// <summary>
|
|
/// The "RoslynTargetsPath" property.
|
|
/// </summary>
|
|
public static readonly string RoslynTargetsPath = "RoslynTargetsPath";
|
|
}
|
|
}
|
|
}
|