Skip to content

Commit

Permalink
Merge pull request #2227 from jkoritzinsky/omnisharp-local-dotnet
Browse files Browse the repository at this point in the history
Add Custom .NET CLI support to OmniSharp
  • Loading branch information
filipw authored Sep 28, 2021
2 parents 657b064 + 4fc9fae commit 02d3168
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 24 deletions.
13 changes: 6 additions & 7 deletions src/OmniSharp.Host/CompositionHostBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ public static IServiceProvider CreateDefaultServiceProvider(
services.AddSingleton<IAnalyzerAssemblyLoader, AnalyzerAssemblyLoader>();
services.AddOptions();

// Setup the options from configuration
services.Configure<OmniSharpOptions>(configuration)
.PostConfigure<OmniSharpOptions>(OmniSharpOptions.PostConfigure);
services.AddSingleton(configuration);
services.AddSingleton<IConfiguration>(configuration);

services.AddSingleton<IDotNetCliService, DotNetCliService>();

// MSBuild
Expand All @@ -149,13 +155,6 @@ public static IServiceProvider CreateDefaultServiceProvider(
assemblyLoader: sp.GetService<IAssemblyLoader>(),
msbuildConfiguration: configuration.GetSection("msbuild")));


// Setup the options from configuration
services.Configure<OmniSharpOptions>(configuration)
.PostConfigure<OmniSharpOptions>(OmniSharpOptions.PostConfigure);
services.AddSingleton(configuration);
services.AddSingleton<IConfiguration>(configuration);

services.AddLogging(builder =>
{
var workspaceInformationServiceName = typeof(WorkspaceInformationService).FullName;
Expand Down
40 changes: 38 additions & 2 deletions src/OmniSharp.Host/Services/DotNetCliService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NuGet.Versioning;
using OmniSharp.Eventing;
using OmniSharp.Options;
using OmniSharp.Utilities;

namespace OmniSharp.Services
Expand All @@ -20,14 +25,45 @@ internal class DotNetCliService : IDotNetCliService

public string DotNetPath { get; }

public DotNetCliService(ILoggerFactory loggerFactory, IEventEmitter eventEmitter, string dotnetPath = null)
public DotNetCliService(ILoggerFactory loggerFactory, IEventEmitter eventEmitter, IOptions<DotNetCliOptions> dotNetCliOptions, IOmniSharpEnvironment environment)
{
_logger = loggerFactory.CreateLogger<DotNetCliService>();
_eventEmitter = eventEmitter;
_locks = new ConcurrentDictionary<string, object>();
_semaphore = new SemaphoreSlim(Environment.ProcessorCount / 2);

DotNetPath = dotnetPath ?? "dotnet";
// Check if any of the provided paths have a dotnet executable.
string executableExtension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;
foreach (var path in dotNetCliOptions.Value.GetNormalizedLocationPaths(environment))
{
if (File.Exists(Path.Combine(path, $"dotnet{executableExtension}")))
{
// We'll take the first path that has a dotnet executable.
DotNetPath = Path.Combine(path, "dotnet");
}
else
{
_logger.LogInformation($"Provided dotnet CLI path does not contain the dotnet executable: '{path}'.");
}
}

// If we still haven't found a dotnet CLI, check the DOTNET_ROOT environment variable.
if (DotNetPath is null)
{
_logger.LogInformation("Checking the 'DOTNET_ROOT' environment variable to find a .NET SDK");
string dotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT");
if (!string.IsNullOrEmpty(dotnetRoot) && File.Exists(Path.Combine(dotnetRoot, $"dotnet{executableExtension}")))
{
DotNetPath = Path.Combine(dotnetRoot, "dotnet");
}
}

// If we still haven't found the CLI, use the one on the PATH.
if (DotNetPath is null)
{
_logger.LogInformation("Using the 'dotnet' on the PATH.");
DotNetPath = "dotnet";
}

_logger.LogInformation($"DotNetPath set to {DotNetPath}");
}
Expand Down
12 changes: 12 additions & 0 deletions src/OmniSharp.Shared/Options/DotNetCliOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OmniSharp.Options
{
public class DotNetCliOptions : OmniSharpExtensionsOptions
{
}
}
3 changes: 3 additions & 0 deletions src/OmniSharp.Shared/Options/OmniSharpOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class OmniSharpOptions

public ImplementTypeOptions ImplementTypeOptions { get; set; } = new ImplementTypeOptions();

public DotNetCliOptions DotNetCliOptions { get; set; } = new DotNetCliOptions();

public OmniSharpExtensionsOptions Plugins { get; set; } = new OmniSharpExtensionsOptions();

public override string ToString() => JsonConvert.SerializeObject(this);
Expand All @@ -26,6 +28,7 @@ public static void PostConfigure(OmniSharpOptions options)
options.FileOptions ??= new FileOptions();
options.RenameOptions ??= new RenameOptions();
options.ImplementTypeOptions ??= new ImplementTypeOptions();
options.DotNetCliOptions ??= new DotNetCliOptions();
options.Plugins ??= new OmniSharpExtensionsOptions();
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/OmniSharp.MSBuild.Tests/ProjectLoadListenerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ public async Task Given_a_restored_project_the_references_are_emitted()
var emitter = new ProjectLoadTestEventEmitter();

using var testProject = await TestAssets.Instance.GetTestProjectAsync("HelloWorld");
var dotnetCliService = new DotNetCliService(LoggerFactory, emitter);
await dotnetCliService.RestoreAsync(testProject.Directory);
using var host = CreateMSBuildTestHost(testProject.Directory, emitter.AsExportDescriptionProvider(LoggerFactory));
var dotnetCliService = host.GetExport<IDotNetCliService>();
await dotnetCliService.RestoreAsync(testProject.Directory);
Assert.Single(emitter.ReceivedMessages);
Assert.NotEmpty(emitter.ReceivedMessages[0].References.Where(reference => reference == GetHashedReference("system.core")));
}
Expand Down
10 changes: 9 additions & 1 deletion tests/OmniSharp.MSBuild.Tests/ProjectWithAnalyzersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using OmniSharp.FileWatching;
using OmniSharp.Models.Diagnostics;
using OmniSharp.Models.FilesChanged;
using OmniSharp.Options;
using OmniSharp.Services;
using TestUtility;
using Xunit;
Expand Down Expand Up @@ -348,7 +349,14 @@ await host.GetFilesChangedService().Handle(new[] {

private static async Task RestoreProject(ITestProject testProject)
{
await new DotNetCliService(new LoggerFactory(), NullEventEmitter.Instance).RestoreAsync(testProject.Directory);
DotNetCliOptions options = new DotNetCliOptions
{
LocationPaths = new[]
{
Path.Combine(TestAssets.Instance.RootFolder, DotNetCliVersion.Current.GetFolderName())
}
};
await new DotNetCliService(new LoggerFactory(), NullEventEmitter.Instance, Microsoft.Extensions.Options.Options.Create(options), new OmniSharpEnvironment(testProject.Directory)).RestoreAsync(testProject.Directory);
}
}
}
25 changes: 13 additions & 12 deletions tests/TestUtility/TestServiceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Options;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces;
using OmniSharp;
using OmniSharp.Eventing;
using OmniSharp.FileWatching;
Expand Down Expand Up @@ -81,7 +83,7 @@ public static IServiceProvider Create(
eventEmitter = eventEmitter ?? NullEventEmitter.Instance;

var assemblyLoader = CreateAssemblyLoader(loggerFactory);
var dotNetCliService = CreateDotNetCliService(dotNetCliVersion, loggerFactory, eventEmitter);
var dotNetCliService = CreateDotNetCliService(dotNetCliVersion, loggerFactory, environment, eventEmitter);
var configuration = CreateConfiguration(configurationData);
var msbuildLocator = CreateMSBuildLocator(loggerFactory, assemblyLoader);
var sharedTextWriter = CreateSharedTextWriter(testOutput);
Expand All @@ -105,7 +107,7 @@ public static IServiceProvider Create(
{
eventEmitter = eventEmitter ?? NullEventEmitter.Instance;

var dotNetCliService = CreateDotNetCliService(dotNetCliVersion, loggerFactory, eventEmitter);
var dotNetCliService = CreateDotNetCliService(dotNetCliVersion, loggerFactory, environment, eventEmitter);
var configuration = CreateConfiguration(configurationData);
var sharedTextWriter = CreateSharedTextWriter(testOutput);

Expand Down Expand Up @@ -142,26 +144,25 @@ private static IConfigurationRoot CreateConfiguration(IConfiguration configurati
return builder.Build();
}

private static IDotNetCliService CreateDotNetCliService(DotNetCliVersion dotNetCliVersion,
ILoggerFactory loggerFactory, IEventEmitter eventEmitter)
private static IDotNetCliService CreateDotNetCliService(
DotNetCliVersion dotNetCliVersion,
ILoggerFactory loggerFactory,
IOmniSharpEnvironment environment,
IEventEmitter eventEmitter)
{
var dotnetPath = Path.Combine(
TestAssets.Instance.RootFolder,
dotNetCliVersion.GetFolderName(),
"dotnet");
dotNetCliVersion.GetFolderName());

if (!File.Exists(dotnetPath))
{
dotnetPath = Path.ChangeExtension(dotnetPath, ".exe");
}
var options = new DotNetCliOptions { LocationPaths = new[] { dotnetPath } };

if (!File.Exists(dotnetPath))
if (!Directory.Exists(dotnetPath))
{
throw new InvalidOperationException(
$"Local .NET CLI path does not exist. Did you run build.(ps1|sh) from the command line?");
}

return new DotNetCliService(loggerFactory, NullEventEmitter.Instance, dotnetPath);
return new DotNetCliService(loggerFactory, NullEventEmitter.Instance, Options.Create(options), environment);
}

private static IMSBuildLocator CreateMSBuildLocator(ILoggerFactory loggerFactory,
Expand Down

0 comments on commit 02d3168

Please sign in to comment.