diff --git a/NuGet.Config b/NuGet.Config index 872c1fe3d5..57859c5533 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,9 +1,9 @@ - + - - - - - - - + + + + + + + \ No newline at end of file diff --git a/src/OmniSharp.Script/CachingScriptMetadataResolver.cs b/src/OmniSharp.Script/CachingScriptMetadataResolver.cs index deee618000..5ff02eed44 100644 --- a/src/OmniSharp.Script/CachingScriptMetadataResolver.cs +++ b/src/OmniSharp.Script/CachingScriptMetadataResolver.cs @@ -1,27 +1,31 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Scripting; namespace OmniSharp.Script { public class CachingScriptMetadataResolver : MetadataReferenceResolver { + private readonly MetadataReferenceResolver _defaultReferenceResolver; private static Dictionary> DirectReferenceCache = new Dictionary>(); private static Dictionary MissingReferenceCache = new Dictionary(); - private static MetadataReferenceResolver _defaultRuntimeResolver = ScriptMetadataResolver.Default; + + public CachingScriptMetadataResolver(MetadataReferenceResolver defaultReferenceResolver) + { + _defaultReferenceResolver = defaultReferenceResolver; + } public override bool Equals(object other) { - return _defaultRuntimeResolver.Equals(other); + return _defaultReferenceResolver.Equals(other); } public override int GetHashCode() { - return _defaultRuntimeResolver.GetHashCode(); + return _defaultReferenceResolver.GetHashCode(); } - public override bool ResolveMissingAssemblies => _defaultRuntimeResolver.ResolveMissingAssemblies; + public override bool ResolveMissingAssemblies => _defaultReferenceResolver.ResolveMissingAssemblies; public override PortableExecutableReference ResolveMissingAssembly(MetadataReference definition, AssemblyIdentity referenceIdentity) { @@ -30,7 +34,7 @@ public override PortableExecutableReference ResolveMissingAssembly(MetadataRefer return MissingReferenceCache[referenceIdentity.Name]; } - var result = _defaultRuntimeResolver.ResolveMissingAssembly(definition, referenceIdentity); + var result = _defaultReferenceResolver.ResolveMissingAssembly(definition, referenceIdentity); if (result != null) { MissingReferenceCache[referenceIdentity.Name] = result; @@ -47,7 +51,7 @@ public override ImmutableArray ResolveReference(str return DirectReferenceCache[key]; } - var result = _defaultRuntimeResolver.ResolveReference(reference, baseFilePath, properties); + var result = _defaultReferenceResolver.ResolveReference(reference, baseFilePath, properties); if (result.Length > 0) { DirectReferenceCache[key] = result; diff --git a/src/OmniSharp.Script/OmniSharp.Script.csproj b/src/OmniSharp.Script/OmniSharp.Script.csproj index e715469547..44bfc5b5a1 100644 --- a/src/OmniSharp.Script/OmniSharp.Script.csproj +++ b/src/OmniSharp.Script/OmniSharp.Script.csproj @@ -15,6 +15,7 @@ + diff --git a/src/OmniSharp.Script/ScriptHelper.cs b/src/OmniSharp.Script/ScriptHelper.cs index d6002b8e51..c7ddc926b1 100644 --- a/src/OmniSharp.Script/ScriptHelper.cs +++ b/src/OmniSharp.Script/ScriptHelper.cs @@ -1,15 +1,19 @@ using System; using System.Collections.Generic; using System.Reflection; +using Dotnet.Script.NuGetMetadataResolver; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting.Hosting; +using Microsoft.Extensions.Configuration; namespace OmniSharp.Script { - public static class ScriptHelper + public class ScriptHelper { + private readonly IConfiguration _configuration; + // aligned with CSI.exe // https://github.com/dotnet/roslyn/blob/version-2.0.0-rc3/src/Interactive/csi/csi.rsp internal static readonly IEnumerable DefaultNamespaces = new[] @@ -28,28 +32,41 @@ public static class ScriptHelper private static readonly CSharpParseOptions ParseOptions = new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.Parse, SourceCodeKind.Script); - private static readonly Lazy CompilationOptions = new Lazy(() => + private readonly Lazy _compilationOptions; + + public ScriptHelper(IConfiguration configuration = null) + { + this._configuration = configuration; + _compilationOptions = new Lazy(CreateCompilationOptions); + } + + private CSharpCompilationOptions CreateCompilationOptions() { var compilationOptions = new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary, usings: DefaultNamespaces, allowUnsafe: true, - metadataReferenceResolver: new CachingScriptMetadataResolver(), + metadataReferenceResolver: + CreateMetadataReferenceResolver(), sourceReferenceResolver: ScriptSourceResolver.Default, - assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default). - WithSpecificDiagnosticOptions(new Dictionary + assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default).WithSpecificDiagnosticOptions( + new Dictionary { // ensure that specific warnings about assembly references are always suppressed // https://github.com/dotnet/roslyn/issues/5501 - { "CS1701", ReportDiagnostic.Suppress }, - { "CS1702", ReportDiagnostic.Suppress }, - { "CS1705", ReportDiagnostic.Suppress } - }); + {"CS1701", ReportDiagnostic.Suppress}, + {"CS1702", ReportDiagnostic.Suppress}, + {"CS1705", ReportDiagnostic.Suppress} + }); - var topLevelBinderFlagsProperty = typeof(CSharpCompilationOptions).GetProperty("TopLevelBinderFlags", BindingFlags.Instance | BindingFlags.NonPublic); - var binderFlagsType = typeof(CSharpCompilationOptions).GetTypeInfo().Assembly.GetType("Microsoft.CodeAnalysis.CSharp.BinderFlags"); + var topLevelBinderFlagsProperty = + typeof(CSharpCompilationOptions).GetProperty("TopLevelBinderFlags", + BindingFlags.Instance | BindingFlags.NonPublic); + var binderFlagsType = typeof(CSharpCompilationOptions).GetTypeInfo().Assembly + .GetType("Microsoft.CodeAnalysis.CSharp.BinderFlags"); - var ignoreCorLibraryDuplicatedTypesMember = binderFlagsType?.GetField("IgnoreCorLibraryDuplicatedTypes", BindingFlags.Static | BindingFlags.Public); + var ignoreCorLibraryDuplicatedTypesMember = + binderFlagsType?.GetField("IgnoreCorLibraryDuplicatedTypes", BindingFlags.Static | BindingFlags.Public); var ignoreCorLibraryDuplicatedTypesValue = ignoreCorLibraryDuplicatedTypesMember?.GetValue(null); if (ignoreCorLibraryDuplicatedTypesValue != null) { @@ -57,9 +74,25 @@ public static class ScriptHelper } return compilationOptions; - }); + } - public static ProjectInfo CreateProject(string csxFileName, IEnumerable references, IEnumerable namespaces = null) + private CachingScriptMetadataResolver CreateMetadataReferenceResolver() + { + bool enableScriptNuGetReferences = false; + + if (_configuration != null) + { + if (!bool.TryParse(_configuration["enableScriptNuGetReferences"], out enableScriptNuGetReferences)) + { + enableScriptNuGetReferences = false; + } + } + + return enableScriptNuGetReferences ? new CachingScriptMetadataResolver(new NuGetMetadataReferenceResolver(ScriptMetadataResolver.Default)) + : new CachingScriptMetadataResolver(ScriptMetadataResolver.Default); + } + + public ProjectInfo CreateProject(string csxFileName, IEnumerable references, IEnumerable namespaces = null) { var project = ProjectInfo.Create( id: ProjectId.CreateNewId(), @@ -67,7 +100,7 @@ public static ProjectInfo CreateProject(string csxFileName, IEnumerable _targetFrameWork = new Lazy(ResolveTargetFramework); [ImportingConstructor] public ScriptProjectSystem(OmniSharpWorkspace workspace, IOmniSharpEnvironment env, ILoggerFactory loggerFactory, MetadataFileReferenceCache metadataFileReferenceCache) @@ -38,6 +41,7 @@ public ScriptProjectSystem(OmniSharpWorkspace workspace, IOmniSharpEnvironment e _env = env; _logger = loggerFactory.CreateLogger(); _projects = new Dictionary(); + _scriptProjectProvider = ScriptProjectProvider.Create(loggerFactory); } public string Key => "Script"; @@ -46,6 +50,8 @@ public ScriptProjectSystem(OmniSharpWorkspace workspace, IOmniSharpEnvironment e public void Initalize(IConfiguration configuration) { + var scriptHelper = new ScriptHelper(configuration); + _logger.LogInformation($"Detecting CSX files in '{_env.TargetDirectory}'."); // Nothing to do if there are no CSX files @@ -60,15 +66,25 @@ public void Initalize(IConfiguration configuration) // explicitly inherit scripting library references to all global script object (InteractiveScriptGlobals) to be recognized var inheritedCompileLibraries = DependencyContext.Default.CompileLibraries.Where(x => - x.Name.ToLowerInvariant().StartsWith("microsoft.codeanalysis")).ToList(); + x.Name.ToLowerInvariant().StartsWith("microsoft.codeanalysis")).ToList(); // explicitly include System.ValueTuple inheritedCompileLibraries.AddRange(DependencyContext.Default.CompileLibraries.Where(x => - x.Name.ToLowerInvariant().StartsWith("system.valuetuple"))); + x.Name.ToLowerInvariant().StartsWith("system.valuetuple"))); var runtimeContexts = File.Exists(Path.Combine(_env.TargetDirectory, "project.json")) ? ProjectContext.CreateContextForEachTarget(_env.TargetDirectory) : null; var commonReferences = new HashSet(); + + if (!bool.TryParse(configuration["enableScriptNuGetReferences"], out var enableScriptNuGetReferences)) + { + enableScriptNuGetReferences = false; + } + + if (enableScriptNuGetReferences && (runtimeContexts == null || runtimeContexts.Any() == false)) + { + runtimeContexts = TryCreateRuntimeContextsFromScriptFiles(); + } // if we have no context, then we also have no dependencies // we can assume desktop framework @@ -81,7 +97,7 @@ public void Initalize(IConfiguration configuration) AddMetadataReference(commonReferences, typeof(Enumerable).GetTypeInfo().Assembly.Location); inheritedCompileLibraries.AddRange(DependencyContext.Default.CompileLibraries.Where(x => - x.Name.ToLowerInvariant().StartsWith("system.runtime"))); + x.Name.ToLowerInvariant().StartsWith("system.runtime"))); } // otherwise we will grab dependencies for the script from the runtime context else @@ -106,7 +122,7 @@ public void Initalize(IConfiguration configuration) { inheritedCompileLibraries.AddRange(DependencyContext.Default.CompileLibraries.Where(x => - x.Name.ToLowerInvariant().StartsWith("system.runtime"))); + x.Name.ToLowerInvariant().StartsWith("system.runtime"))); } } @@ -124,7 +140,7 @@ public void Initalize(IConfiguration configuration) try { var csxFileName = Path.GetFileName(csxPath); - var project = ScriptHelper.CreateProject(csxFileName, commonReferences); + var project = scriptHelper.CreateProject(csxFileName, commonReferences); // add CSX project to workspace _workspace.AddProject(project); @@ -139,6 +155,21 @@ public void Initalize(IConfiguration configuration) } } + private IEnumerable TryCreateRuntimeContextsFromScriptFiles() + { + _logger.LogInformation($"Attempting to create runtime context from script files. Default target framework {_targetFrameWork.Value}"); + try + { + var scriptProjectInfo = _scriptProjectProvider.CreateProject(_env.TargetDirectory, _targetFrameWork.Value); + return ProjectContext.CreateContextForEachTarget(Path.GetDirectoryName(scriptProjectInfo.PathToProjectJson)); + } + catch (Exception exception) + { + _logger.LogError(exception, "Unable to create runtime context from script files."); + } + return null; + } + private void AddMetadataReference(ISet referenceCollection, string fileReference) { if (!File.Exists(fileReference)) @@ -201,5 +232,13 @@ Task IProjectSystem.GetWorkspaceModelAsync(WorkspaceInformationRequest r } return Task.FromResult(new ScriptContextModelCollection(scriptContextModels)); } + + private static string ResolveTargetFramework() + { + return Assembly.GetEntryAssembly().GetCustomAttributes() + .OfType() + .Select(x => x.FrameworkName) + .FirstOrDefault(); + } } -} +} \ No newline at end of file diff --git a/src/OmniSharp/config.json b/src/OmniSharp/config.json index 4fc409d945..54c6f12daa 100644 --- a/src/OmniSharp/config.json +++ b/src/OmniSharp/config.json @@ -10,5 +10,8 @@ "useTabs": false, "tabSize": 4, "indentationSize": 4 + }, + "script": { + "enableScriptNuGetReferences" : false } } diff --git a/tests/TestUtility/TestHelpers.cs b/tests/TestUtility/TestHelpers.cs index aa4dad6d8b..04e56bb65f 100644 --- a/tests/TestUtility/TestHelpers.cs +++ b/tests/TestUtility/TestHelpers.cs @@ -22,7 +22,8 @@ public static OmniSharpWorkspace CreateCsxWorkspace(TestFile testFile) public static void AddCsxProjectToWorkspace(OmniSharpWorkspace workspace, TestFile testFile) { var references = GetReferences(); - var project = ScriptHelper.CreateProject(testFile.FileName, references.Union(new[] { MetadataReference.CreateFromFile(typeof(CommandLineScriptGlobals).GetTypeInfo().Assembly.Location) }), Enumerable.Empty()); + var scriptHelper = new ScriptHelper(); + var project = scriptHelper.CreateProject(testFile.FileName, references.Union(new[] { MetadataReference.CreateFromFile(typeof(CommandLineScriptGlobals).GetTypeInfo().Assembly.Location) }), Enumerable.Empty()); workspace.AddProject(project); var documentInfo = DocumentInfo.Create(