Skip to content

Commit

Permalink
Some refactoring and a better approach to #23 (resolves #25)
Browse files Browse the repository at this point in the history
  • Loading branch information
daveaglick committed Oct 25, 2017
1 parent d205d31 commit e2c70c1
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 116 deletions.
1 change: 1 addition & 0 deletions src/Buildalyzer/AnalyzerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.Build.Construction;
using System.Linq;
using System.Xml.Linq;
using Buildalyzer.Logging;

namespace Buildalyzer
{
Expand Down
37 changes: 0 additions & 37 deletions src/Buildalyzer/BuildEnvironment.cs

This file was deleted.

51 changes: 51 additions & 0 deletions src/Buildalyzer/Environment/BuildEnvironment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;

namespace Buildalyzer.Environment
{
internal abstract class BuildEnvironment
{
private string _oldMsBuildExtensionsPath = null;
private string _oldMsBuildSdksPath = null;

public abstract string GetToolsPath();

public virtual Dictionary<string, string> GetGlobalProperties(string solutionDir) =>
new Dictionary<string, string>
{
{ MsBuildProperties.SolutionDir, solutionDir },
{ MsBuildProperties.DesignTimeBuild, "true" },
{ MsBuildProperties.BuildProjectReferences, "false" },
{ MsBuildProperties.SkipCompilerExecution, "true" },
{ MsBuildProperties.ProvideCommandLineArgs, "true" },
// Workaround for a problem with resource files, see https://github.com/dotnet/sdk/issues/346#issuecomment-257654120
{ MsBuildProperties.GenerateResourceMSBuildArchitecture, "CurrentArchitecture" }
};

public virtual void SetEnvironmentVars(IReadOnlyDictionary<string, string> globalProperties)
{
if (globalProperties.TryGetValue(MsBuildProperties.MSBuildExtensionsPath, out var msBuildExtensionsPath))
{
_oldMsBuildExtensionsPath = System.Environment.GetEnvironmentVariable(MsBuildProperties.MSBuildExtensionsPath);
System.Environment.SetEnvironmentVariable(MsBuildProperties.MSBuildExtensionsPath, msBuildExtensionsPath);
}
if (globalProperties.TryGetValue(MsBuildProperties.MSBuildSDKsPath, out var msBuildSDKsPath))
{
_oldMsBuildSdksPath = System.Environment.GetEnvironmentVariable(MsBuildProperties.MSBuildSDKsPath);
System.Environment.SetEnvironmentVariable(MsBuildProperties.MSBuildSDKsPath, msBuildSDKsPath);
}
}

public virtual void UnsetEnvironmentVars()
{
if (_oldMsBuildExtensionsPath != null)
{
System.Environment.SetEnvironmentVariable(MsBuildProperties.MSBuildExtensionsPath, _oldMsBuildExtensionsPath);
}
if (_oldMsBuildSdksPath != null)
{
System.Environment.SetEnvironmentVariable(MsBuildProperties.MSBuildSDKsPath, _oldMsBuildSdksPath);
}
}
}
}
38 changes: 38 additions & 0 deletions src/Buildalyzer/Environment/CoreEnvironment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;

namespace Buildalyzer.Environment
{

// Based on code from OmniSharp
// https://github.com/OmniSharp/omnisharp-roslyn/blob/78ccc8b4376c73da282a600ac6fb10fce8620b52/src/OmniSharp.Abstractions/Services/DotNetCliService.cs
internal class CoreEnvironment : BuildEnvironment
{
public string ToolsPath { get; }
public string ExtensionsPath { get; }
public string SDKsPath { get; }
public string RoslynTargetsPath { get; }

public CoreEnvironment(string projectPath)
{
string dotnetPath = DotnetPathResolver.ResolvePath(projectPath);
ToolsPath = dotnetPath;
ExtensionsPath = dotnetPath;
SDKsPath = Path.Combine(dotnetPath, "Sdks");
RoslynTargetsPath = Path.Combine(dotnetPath, "Roslyn");
}

public override string GetToolsPath() => ToolsPath;

public override Dictionary<string, string> GetGlobalProperties(string solutionDir)
{
Dictionary<string, string> globalProperties = base.GetGlobalProperties(solutionDir);
globalProperties.Add(MsBuildProperties.MSBuildExtensionsPath, ExtensionsPath);
globalProperties.Add(MsBuildProperties.MSBuildSDKsPath, SDKsPath);
globalProperties.Add(MsBuildProperties.RoslynTargetsPath, RoslynTargetsPath);
return globalProperties;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace Buildalyzer
namespace Buildalyzer.Environment
{
// Based on code from OmniSharp
// https://github.com/OmniSharp/omnisharp-roslyn/blob/78ccc8b4376c73da282a600ac6fb10fce8620b52/src/OmniSharp.Abstractions/Services/DotNetCliService.cs
internal class CorePathHelper : IPathHelper
internal static class DotnetPathResolver
{
const string DOTNET_CLI_UI_LANGUAGE = nameof(DOTNET_CLI_UI_LANGUAGE);

public string ToolsPath { get; }
public string ExtensionsPath { get; }
public string SDKsPath { get; }
public string RoslynTargetsPath { get; }
private static readonly object BasePathLock = new object();
private static string BasePath = null;

public CorePathHelper(string projectPath)
public static string ResolvePath(string projectPath)
{
// Need to rety calling "dotnet --info" and do a hacky timeout for the process otherwise it occasionally locks up during testing (and possibly in the field)
List<string> lines = GetInfo(projectPath);
int retry = 0;
do
lock(BasePathLock)
{
lines = GetInfo(projectPath);
retry++;
} while ((lines == null || lines.Count == 0) && retry < 5);
string basePath = ParseBasePath(lines);
ToolsPath = basePath;
ExtensionsPath = basePath;
SDKsPath = Path.Combine(basePath, "Sdks");
RoslynTargetsPath = Path.Combine(basePath, "Roslyn");
if(BasePath != null)
{
return BasePath;
}

// Need to rety calling "dotnet --info" and do a hacky timeout for the process otherwise it occasionally locks up during testing (and possibly in the field)
List<string> lines = GetInfo(projectPath);
int retry = 0;
do
{
lines = GetInfo(projectPath);
retry++;
} while ((lines == null || lines.Count == 0) && retry < 5);
BasePath = ParseBasePath(lines);

return BasePath;
}
}

private static List<string> GetInfo(string projectPath)
{
// Ensure that we set the DOTNET_CLI_UI_LANGUAGE environment variable to "en-US" before
// running 'dotnet --info'. Otherwise, we may get localized results.
string originalCliLanguage = Environment.GetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE);
Environment.SetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE, "en-US");
string originalCliLanguage = System.Environment.GetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE);
System.Environment.SetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE, "en-US");

try
{
Expand All @@ -64,7 +64,7 @@ private static List<string> GetInfo(string projectPath)
sw.Start();
while (!process.HasExited)
{
if(sw.ElapsedMilliseconds > 1000)
if (sw.ElapsedMilliseconds > 1000)
{
break;
}
Expand All @@ -75,7 +75,7 @@ private static List<string> GetInfo(string projectPath)
}
finally
{
Environment.SetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE, originalCliLanguage);
System.Environment.SetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE, originalCliLanguage);
}
}

Expand All @@ -89,22 +89,22 @@ private static string ParseBasePath(List<string> lines)
foreach (string line in lines)
{
int colonIndex = line.IndexOf(':');
if (colonIndex >= 0
if (colonIndex >= 0
&& line.Substring(0, colonIndex).Trim().Equals("Base Path", StringComparison.OrdinalIgnoreCase))
{
string basePath = line.Substring(colonIndex + 1).Trim();

// Make sure the base path matches the runtime architecture if on Windows
// Note that this only works for the default installation locations under "Program Files"
if(basePath.Contains(@"\Program Files\") && !Environment.Is64BitProcess)
if (basePath.Contains(@"\Program Files\") && !System.Environment.Is64BitProcess)
{
string newBasePath = basePath.Replace(@"\Program Files\", @"\Program Files (x86)\");
if(Directory.Exists(newBasePath))
if (Directory.Exists(newBasePath))
{
basePath = newBasePath;
}
}
else if(basePath.Contains(@"\Program Files (x86)\") && Environment.Is64BitProcess)
else if (basePath.Contains(@"\Program Files (x86)\") && System.Environment.Is64BitProcess)
{
string newBasePath = basePath.Replace(@"\Program Files (x86)\", @"\Program Files\");
if (Directory.Exists(newBasePath))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
using System.Xml.Linq;
using Microsoft.Build.Utilities;

namespace Buildalyzer
namespace Buildalyzer.Environment
{
internal abstract class PathHelperFactory
internal abstract class EnvironmentFactory
{
public static IPathHelper GetPathHelper(string projectPath, XDocument projectDocument)
public static BuildEnvironment GetBuildEnvironment(string projectPath, XDocument projectDocument)
{
// If we're running on .NET Core, use the .NET Core SDK regardless of the project file
if (System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription
.Replace(" ", "").StartsWith(".NETCore", StringComparison.OrdinalIgnoreCase))
{
return new CorePathHelper(projectPath);
return new CoreEnvironment(projectPath);
}

// Look at the project file to determine
Expand All @@ -36,17 +36,17 @@ public static IPathHelper GetPathHelper(string projectPath, XDocument projectDoc
&& targetFramework.Length > 3
&& char.IsDigit(targetFramework[4]))
{
return new FrameworkPathHelper();
return new FrameworkEnvironment(projectPath, true);
}

// Otherwise use the .NET Core SDK
return new CorePathHelper(projectPath);
return new CoreEnvironment(projectPath);
}

// Use Framework tools if a ToolsVersion attribute
if (projectElement.GetAttributeValue("ToolsVersion") != null)
{
return new FrameworkPathHelper();
return new FrameworkEnvironment(projectPath, false);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,39 @@
using System.IO;
using System.Linq;
using Microsoft.Build.Utilities;
using System.Collections.Generic;

namespace Buildalyzer
namespace Buildalyzer.Environment
{
internal class FrameworkPathHelper : IPathHelper
internal class FrameworkEnvironment : BuildEnvironment
{
private readonly bool _sdkProject;

public string ToolsPath { get; }
public string ExtensionsPath { get; }
public string SDKsPath { get; }
public string RoslynTargetsPath { get; }

public FrameworkPathHelper()
public FrameworkEnvironment(string projectPath, bool sdkProject)
{
ToolsPath = GetToolsPath();
ToolsPath = LocateToolsPath();
ExtensionsPath = Path.GetFullPath(Path.Combine(ToolsPath, @"..\..\"));
SDKsPath = Path.Combine(ExtensionsPath, "Sdks");
SDKsPath = Path.Combine(sdkProject ? DotnetPathResolver.ResolvePath(projectPath) : ExtensionsPath, "Sdks");
RoslynTargetsPath = Path.Combine(ToolsPath, "Roslyn");
}

private static string GetToolsPath()
public override string GetToolsPath() => ToolsPath;

public override Dictionary<string, string> GetGlobalProperties(string solutionDir)
{
Dictionary<string, string> globalProperties = base.GetGlobalProperties(solutionDir);
globalProperties.Add(MsBuildProperties.MSBuildExtensionsPath, ExtensionsPath);
globalProperties.Add(MsBuildProperties.MSBuildSDKsPath, SDKsPath);
globalProperties.Add(MsBuildProperties.RoslynTargetsPath, RoslynTargetsPath);
return globalProperties;
}

private static string LocateToolsPath()
{
string toolsPath = ToolLocationHelper.GetPathToBuildToolsFile("msbuild.exe", ToolLocationHelper.CurrentToolsVersion);
if (string.IsNullOrEmpty(toolsPath))
Expand All @@ -39,7 +53,7 @@ private static string GetToolsPath()
// From https://github.com/KirillOsenkov/MSBuildStructuredLog/blob/4649f55f900a324421bad5a714a2584926a02138/src/StructuredLogViewer/MSBuildLocator.cs
private static string PollForToolsPath()
{
string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
string programFilesX86 = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86);
return new[]
{
Path.Combine(programFilesX86, @"Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Buildalyzer
namespace Buildalyzer.Environment
{
public static class MsBuildProperties
{
Expand Down
10 changes: 0 additions & 10 deletions src/Buildalyzer/IPathHelper.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System;
using System.Text;
using Microsoft.Extensions.Logging;
using System.IO;

namespace Buildalyzer
namespace Buildalyzer.Logging
{
internal class TextWriterLogger : ILogger
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using Microsoft.Extensions.Logging;
using System.IO;

namespace Buildalyzer
namespace Buildalyzer.Logging
{
public class TextWriterLoggerProvider : ILoggerProvider
{
Expand Down
Loading

0 comments on commit e2c70c1

Please sign in to comment.