Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-architect how MSBuild is loaded by OmniSharp #988

Merged
merged 17 commits into from
Oct 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 64 additions & 23 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -245,60 +245,102 @@ Task("CreateMSBuildFolder")
{
DirectoryHelper.ForceCreate(env.Folders.MSBuild);
var msbuild15TargetFolder = CombinePaths(env.Folders.MSBuild, "15.0");
var msbuild15BinTargetFolder = CombinePaths(msbuild15TargetFolder, "Bin");
var msbuildLibraries = new []
{
"Microsoft.Build",
"Microsoft.Build.Framework",
"Microsoft.Build.Tasks.Core",
"Microsoft.Build.Utilities.Core"
};
string sdkResolverTFM;
if (Platform.Current.IsWindows)
{
Information("Copying MSBuild runtime...");
var msbuildRuntimeFolder = CombinePaths(env.Folders.Tools, "Microsoft.Build.Runtime", "contentFiles", "any", "net46");
DirectoryHelper.Copy(msbuildRuntimeFolder, env.Folders.MSBuild);
var msbuildSourceFolder = CombinePaths(env.Folders.Tools, "Microsoft.Build.Runtime", "contentFiles", "any", "net46");
DirectoryHelper.Copy(msbuildSourceFolder, msbuild15BinTargetFolder, copySubDirectories: false);
var msbuild15SourceFolder = CombinePaths(msbuildSourceFolder, "15.0");
DirectoryHelper.Copy(msbuild15SourceFolder, msbuild15TargetFolder);
Information("Copying MSBuild libraries...");
foreach (var library in msbuildLibraries)
{
var libraryFileName = library + ".dll";
var librarySourcePath = CombinePaths(env.Folders.Tools, library, "lib", "net46", libraryFileName);
var libraryTargetPath = CombinePaths(msbuild15BinTargetFolder, libraryFileName);
FileHelper.Copy(librarySourcePath, libraryTargetPath);
}
sdkResolverTFM = "net46";
}
else
{
Information("Copying Mono MSBuild runtime...");
DirectoryHelper.Copy(env.Folders.MonoMSBuildRuntime, env.Folders.MSBuild);
var msbuildSourceFolder = env.Folders.MonoMSBuildRuntime;
DirectoryHelper.Copy(msbuildSourceFolder, msbuild15BinTargetFolder, copySubDirectories: false);
var msbuild15SourceFolder = CombinePaths(msbuildSourceFolder, "15.0");
DirectoryHelper.Copy(msbuild15SourceFolder, msbuild15TargetFolder);
Information("Copying MSBuild libraries...");
foreach (var library in msbuildLibraries)
{
var libraryFileName = library + ".dll";
var librarySourcePath = CombinePaths(env.Folders.MonoMSBuildLib, libraryFileName);
var libraryTargetPath = CombinePaths(msbuild15BinTargetFolder, libraryFileName);
FileHelper.Copy(librarySourcePath, libraryTargetPath);
}
sdkResolverTFM = "netstandard1.5";
}
// Copy MSBuild SDK Resolver and DotNetHostResolver
Information("Coping MSBuild SDK resolver...");
var sdkResolverFolder = CombinePaths(env.Folders.Tools, "Microsoft.DotNet.MSBuildSdkResolver", "lib", sdkResolverTFM);
var msbuildSdkResolverFolder = CombinePaths(env.Folders.MSBuild, "SdkResolvers", "Microsoft.DotNet.MSBuildSdkResolver");
DirectoryHelper.ForceCreate(msbuildSdkResolverFolder);
var sdkResolverSourceFolder = CombinePaths(env.Folders.Tools, "Microsoft.DotNet.MSBuildSdkResolver", "lib", sdkResolverTFM);
var sdkResolverTargetFolder = CombinePaths(msbuild15BinTargetFolder, "SdkResolvers", "Microsoft.DotNet.MSBuildSdkResolver");
DirectoryHelper.ForceCreate(sdkResolverTargetFolder);
FileHelper.Copy(
source: CombinePaths(sdkResolverFolder, "Microsoft.DotNet.MSBuildSdkResolver.dll"),
destination: CombinePaths(msbuildSdkResolverFolder, "Microsoft.DotNet.MSBuildSdkResolver.dll"));
source: CombinePaths(sdkResolverSourceFolder, "Microsoft.DotNet.MSBuildSdkResolver.dll"),
destination: CombinePaths(sdkResolverTargetFolder, "Microsoft.DotNet.MSBuildSdkResolver.dll"));
if (Platform.Current.IsWindows)
{
CopyDotNetHostResolver(env, "win", "x86", "hostfxr.dll", msbuildSdkResolverFolder, copyToArchSpecificFolder: true);
CopyDotNetHostResolver(env, "win", "x64", "hostfxr.dll", msbuildSdkResolverFolder, copyToArchSpecificFolder: true);
CopyDotNetHostResolver(env, "win", "x86", "hostfxr.dll", sdkResolverTargetFolder, copyToArchSpecificFolder: true);
CopyDotNetHostResolver(env, "win", "x64", "hostfxr.dll", sdkResolverTargetFolder, copyToArchSpecificFolder: true);
}
else if (Platform.Current.IsMacOS)
{
CopyDotNetHostResolver(env, "osx", "x64", "libhostfxr.dylib", msbuildSdkResolverFolder, copyToArchSpecificFolder: false);
CopyDotNetHostResolver(env, "osx", "x64", "libhostfxr.dylib", sdkResolverTargetFolder, copyToArchSpecificFolder: false);
}
else if (Platform.Current.IsLinux)
{
CopyDotNetHostResolver(env, "linux", "x64", "libhostfxr.so", msbuildSdkResolverFolder, copyToArchSpecificFolder: false);
CopyDotNetHostResolver(env, "linux", "x64", "libhostfxr.so", sdkResolverTargetFolder, copyToArchSpecificFolder: false);
}
// Copy content of Microsoft.Net.Compilers
Information("Copying Microsoft.Net.Compilers...");
var compilersFolder = CombinePaths(env.Folders.Tools, "Microsoft.Net.Compilers", "tools");
var msbuildRoslynFolder = CombinePaths(env.Folders.MSBuild, "Roslyn");
var compilersSourceFolder = CombinePaths(env.Folders.Tools, "Microsoft.Net.Compilers", "tools");
var compilersTargetFolder = CombinePaths(msbuild15BinTargetFolder, "Roslyn");
DirectoryHelper.Copy(compilersFolder, msbuildRoslynFolder);
DirectoryHelper.Copy(compilersSourceFolder, compilersTargetFolder);
// Delete unnecessary files
FileHelper.Delete(CombinePaths(msbuildRoslynFolder, "Microsoft.CodeAnalysis.VisualBasic.dll"));
FileHelper.Delete(CombinePaths(msbuildRoslynFolder, "Microsoft.VisualBasic.Core.targets"));
FileHelper.Delete(CombinePaths(msbuildRoslynFolder, "VBCSCompiler.exe"));
FileHelper.Delete(CombinePaths(msbuildRoslynFolder, "VBCSCompiler.exe.config"));
FileHelper.Delete(CombinePaths(msbuildRoslynFolder, "vbc.exe"));
FileHelper.Delete(CombinePaths(msbuildRoslynFolder, "vbc.exe.config"));
FileHelper.Delete(CombinePaths(msbuildRoslynFolder, "vbc.rsp"));
FileHelper.Delete(CombinePaths(compilersTargetFolder, "Microsoft.CodeAnalysis.VisualBasic.dll"));
FileHelper.Delete(CombinePaths(compilersTargetFolder, "Microsoft.VisualBasic.Core.targets"));
FileHelper.Delete(CombinePaths(compilersTargetFolder, "VBCSCompiler.exe"));
FileHelper.Delete(CombinePaths(compilersTargetFolder, "VBCSCompiler.exe.config"));
FileHelper.Delete(CombinePaths(compilersTargetFolder, "vbc.exe"));
FileHelper.Delete(CombinePaths(compilersTargetFolder, "vbc.exe.config"));
FileHelper.Delete(CombinePaths(compilersTargetFolder, "vbc.rsp"));
});

/// <summary>
Expand Down Expand Up @@ -514,7 +556,6 @@ void CopyMonoBuild(BuildEnvironment env, string sourceFolder, string outputFolde

// Copy MSBuild runtime and libraries
DirectoryHelper.Copy($"{env.Folders.MSBuild}", CombinePaths(outputFolder, "msbuild"));
DirectoryHelper.Copy($"{env.Folders.MonoMSBuildLib}", outputFolder);

// Included in Mono
FileHelper.Delete(CombinePaths(outputFolder, "System.AppContext.dll"));
Expand Down
3 changes: 3 additions & 0 deletions build/Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@
<MicrosoftExtensionsPlatformAbstractionsVersion>1.1.0</MicrosoftExtensionsPlatformAbstractionsVersion>
<MicrosoftNetTestSdkVersion>15.0.0</MicrosoftNetTestSdkVersion>
<MicrosoftTestPlatformTranslationLayerVersion>15.3.0</MicrosoftTestPlatformTranslationLayerVersion>
<MicrosoftVisualStudioSetupConfigurationInteropVersion>1.14.114</MicrosoftVisualStudioSetupConfigurationInteropVersion>
<MicrosoftVisualStudioSDKEmbedInteropTypesVersion>15.0.12</MicrosoftVisualStudioSDKEmbedInteropTypesVersion>
<NewtonsoftJsonVersion>9.0.1</NewtonsoftJsonVersion>
<NuGetPackagingVersion>4.3.0</NuGetPackagingVersion>
<NuGetPackagingCoreVersion>4.3.0</NuGetPackagingCoreVersion>
<NuGetProjectModelVersion>4.3.0</NuGetProjectModelVersion>
<NuGetVersioningVersion>4.3.0</NuGetVersioningVersion>
<SystemCollectionsImmutableVersion>1.4.0</SystemCollectionsImmutableVersion>
<SystemCompositionVersion>1.0.31</SystemCompositionVersion>
<SystemReflectionMetadataVersion>1.4.2</SystemReflectionMetadataVersion>
<SystemThreadingTasksDataflowVersion>4.6.0</SystemThreadingTasksDataflowVersion>
Expand Down
28 changes: 27 additions & 1 deletion scripts/runhelpers.cake
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,32 @@ private void KillProcessTree(Process process)
}
else
{
process.Kill();
foreach (var pid in GetUnixChildProcessIds(process.Id))
{
Run("kill", pid.ToString());
}

Run("kill", process.Id.ToString());
}
}

int[] GetUnixChildProcessIds(int processId)
{
var output = RunAndCaptureOutput("ps", "-A -o ppid,pid");
var lines = output.Split(new[] { System.Environment.NewLine }, System.StringSplitOptions.RemoveEmptyEntries);
var childPIDs = new List<int>();

foreach (var line in lines)
{
var pairs = line.Trim().Split(new[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries);

int ppid;
if (int.TryParse(pairs[0].Trim(), out ppid) && ppid == processId)
{
childPIDs.Add(int.Parse(pairs[1].Trim()));
}

}

return childPIDs.ToArray();
}
5 changes: 5 additions & 0 deletions src/OmniSharp.Abstractions/Logging/LoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ namespace Microsoft.Extensions.Logging
{
public static class LoggerExceptions
{
public static void LogDebug(this ILogger logger, Exception ex, string message, params object[] args)
{
logger.LogDebug(0, ex, message, args);
}

public static void LogError(this ILogger logger, Exception ex, string message, params object[] args)
{
logger.LogError(0, ex, message, args);
Expand Down
10 changes: 10 additions & 0 deletions src/OmniSharp.Abstractions/MSBuild/Discovery/DiscoveryType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace OmniSharp.MSBuild.Discovery
{
public enum DiscoveryType
{
StandAlone = 0,
DeveloperConsole = 1,
VisualStudioSetup = 2,
Mono = 3
}
}
12 changes: 12 additions & 0 deletions src/OmniSharp.Abstractions/MSBuild/Discovery/IMSBuildLocator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Immutable;

namespace OmniSharp.MSBuild.Discovery
{
public interface IMSBuildLocator
{
MSBuildInstance RegisteredInstance { get; }

void RegisterInstance(MSBuildInstance instance);
ImmutableArray<MSBuildInstance> GetInstances();
}
}
31 changes: 31 additions & 0 deletions src/OmniSharp.Abstractions/MSBuild/Discovery/MSBuildInstance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Immutable;

namespace OmniSharp.MSBuild.Discovery
{
public class MSBuildInstance
{
public string Name { get; }
public string MSBuildPath { get; }
public Version Version { get; }
public DiscoveryType DiscoveryType { get; }
public ImmutableDictionary<string, string> PropertyOverrides { get; }
public bool SetMSBuildExePathVariable { get; }

public MSBuildInstance(
string name, string msbuildPath, Version version, DiscoveryType discoveryType,
ImmutableDictionary<string, string> propertyOverrides = null,
bool setMSBuildExePathVariable = false)
{
Name = name;
MSBuildPath = msbuildPath;
Version = version ?? new Version(0, 0);
DiscoveryType = discoveryType;
PropertyOverrides = propertyOverrides ?? ImmutableDictionary<string, string>.Empty;
SetMSBuildExePathVariable = setMSBuildExePathVariable;
}

public override string ToString()
=> $"{Name} {Version} - \"{MSBuildPath}\"";
}
}
1 change: 1 addition & 0 deletions src/OmniSharp.Abstractions/OmniSharp.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="$(MicrosoftExtensionsPlatformAbstractionsVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
<PackageReference Include="NuGet.Versioning" Version="$(NuGetVersioningVersion)" />
<PackageReference Include="System.Collections.Immutable" Version="$(SystemCollectionsImmutableVersion)" />
<PackageReference Include="System.Composition" Version="$(SystemCompositionVersion)" />
<PackageReference Include="System.ValueTuple" Version="$(SystemValueTupleVersion)" />
</ItemGroup>
Expand Down
26 changes: 26 additions & 0 deletions src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,32 @@ private static string RealPath(string path)
return result;
}

public static Version GetMonoVersion()
{
var output = ProcessHelper.RunAndCaptureOutput("mono", "--version");
if (output == null)
{
return null;
}

// The mono --version text contains several lines. We'll just walk through the first line,
// word by word, until we find a word that parses as a version number. Normally, this should
// be the *fifth* word. E.g. "Mono JIT compiler version 4.8.0"

var lines = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
var words = lines[0].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

foreach (var word in words)
{
if (Version.TryParse(word, out var version))
{
return version;
}
}

return null;
}

public static string GetMonoRuntimePath()
{
if (IsWindows)
Expand Down
3 changes: 2 additions & 1 deletion src/OmniSharp.Host/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("TestUtility")]
[assembly: InternalsVisibleTo("OmniSharp.Http.Tests")]
[assembly: InternalsVisibleTo("TestUtility")]
49 changes: 43 additions & 6 deletions src/OmniSharp.Host/CompositionHostBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using OmniSharp.Eventing;
using OmniSharp.FileWatching;
using OmniSharp.Mef;
using OmniSharp.MSBuild.Discovery;
using OmniSharp.Options;
using OmniSharp.Roslyn;
using OmniSharp.Services;
Expand Down Expand Up @@ -49,15 +50,24 @@ public CompositionHost Build()
var assemblyLoader = _serviceProvider.GetRequiredService<IAssemblyLoader>();
var config = new ContainerConfiguration();

var assemblies = _assemblies
.Concat(new[] { typeof(OmniSharpWorkspace).GetTypeInfo().Assembly, typeof(IRequest).GetTypeInfo().Assembly })
.Distinct();

config = config.WithAssemblies(assemblies);

var fileSystemWatcher = new ManualFileSystemWatcher();
var metadataHelper = new MetadataHelper(assemblyLoader);

// We must register an MSBuild instance before composing MEF to ensure that
// our AssemblyResolve event is hooked up first.
var msbuildLocator = _serviceProvider.GetRequiredService<IMSBuildLocator>();
var instances = msbuildLocator.GetInstances();
var instance = instances.FirstOrDefault();
if (instance != null)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we supposed to do anything if we couldn't locate any instances?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably report an error to the log. Will add that.

{
msbuildLocator.RegisterInstance(instance);
}
else
{
var logger = loggerFactory.CreateLogger<CompositionHostBuilder>();
logger.LogError("Could not locate MSBuild instance to register with OmniSharp");
}

config = config
.WithProvider(MefValueProvider.From(_serviceProvider))
.WithProvider(MefValueProvider.From<IFileSystemWatcher>(fileSystemWatcher))
Expand All @@ -69,11 +79,32 @@ public CompositionHost Build()
.WithProvider(MefValueProvider.From(options.CurrentValue.FormattingOptions))
.WithProvider(MefValueProvider.From(assemblyLoader))
.WithProvider(MefValueProvider.From(metadataHelper))
.WithProvider(MefValueProvider.From(msbuildLocator))
.WithProvider(MefValueProvider.From(_eventEmitter ?? NullEventEmitter.Instance));

var parts = _assemblies
.Concat(new[] { typeof(OmniSharpWorkspace).GetTypeInfo().Assembly, typeof(IRequest).GetTypeInfo().Assembly })
.Distinct()
.SelectMany(a => SafeGetTypes(a))
.ToArray();

config = config.WithParts(parts);

return config.CreateContainer();
}

private static IEnumerable<Type> SafeGetTypes(Assembly a)
{
try
{
return a.DefinedTypes.Select(t => t.AsType()).ToArray();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null).ToArray();
}
}

public static IServiceProvider CreateDefaultServiceProvider(IConfiguration configuration, IServiceCollection services = null)
{
services = services ?? new ServiceCollection();
Expand All @@ -83,6 +114,12 @@ public static IServiceProvider CreateDefaultServiceProvider(IConfiguration confi
services.AddSingleton<IAssemblyLoader, AssemblyLoader>();
services.AddOptions();

// MSBuild
services.AddSingleton<IMSBuildLocator>(sp =>
MSBuildLocator.CreateDefault(
loggerFactory: sp.GetService<ILoggerFactory>(),
assemblyLoader: sp.GetService<IAssemblyLoader>()));

// Setup the options from configuration
services.Configure<OmniSharpOptions>(configuration);
services.AddLogging();
Expand Down
Loading