diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs index 70394e8e49be155daad9e270b5483e3042553a2e..bd06f5f9b57f6ab76baba146c9f4289104f517a7 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CSharpCompilationOptionsTests.cs @@ -81,7 +81,7 @@ public void Invariants() TestProperty((old, value) => old.WithExtendedCustomDebugInformation(value), opt => opt.ExtendedCustomDebugInformation, false); TestProperty((old, value) => old.WithXmlReferenceResolver(value), opt => opt.XmlReferenceResolver, new XmlFileResolver(null)); - TestProperty((old, value) => old.WithMetadataReferenceResolver(value), opt => opt.MetadataReferenceResolver, new AssemblyReferenceResolver(new MetadataFileReferenceResolver(new string[0], null), new MetadataFileReferenceProvider())); + TestProperty((old, value) => old.WithMetadataReferenceResolver(value), opt => opt.MetadataReferenceResolver, new AssemblyReferenceResolver(MetadataFileReferenceResolver.Default, new MetadataFileReferenceProvider())); TestProperty((old, value) => old.WithAssemblyIdentityComparer(value), opt => opt.AssemblyIdentityComparer, new DesktopAssemblyIdentityComparer(new AssemblyPortabilityPolicy())); TestProperty((old, value) => old.WithStrongNameProvider(value), opt => opt.StrongNameProvider, new DesktopStrongNameProvider()); } @@ -347,7 +347,7 @@ private static CSharpCompilationOptions CreateCSharpCompilationOptions() bool extendedCustomDebugInformation = true; XmlReferenceResolver xmlReferenceResolver = new XmlFileResolver(null); SourceReferenceResolver sourceReferenceResolver = new SourceFileResolver(ImmutableArray.Empty, null); - MetadataReferenceResolver metadataReferenceResolver = new AssemblyReferenceResolver(new MetadataFileReferenceResolver(ImmutableArray.Empty, null), MetadataFileReferenceProvider.Default); + MetadataReferenceResolver metadataReferenceResolver = new AssemblyReferenceResolver(MetadataFileReferenceResolver.Default, MetadataFileReferenceProvider.Default); AssemblyIdentityComparer assemblyIdentityComparer = AssemblyIdentityComparer.Default; // Currently uses reference equality StrongNameProvider strongNameProvider = new DesktopStrongNameProvider(); MetadataImportOptions metadataImportOptions = 0; diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs index 27022bc149e25152ed9c66ca90b41eae84c4e135..d2319095efac44bf9e72fb948e74a3bac2bee326 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs @@ -1587,7 +1587,7 @@ public void ReferenceManagerReuse_WithMetadataReferenceResolver() var c1 = CSharpCompilation.Create("c", options: TestOptions.ReleaseDll); var c2 = c1.WithOptions(TestOptions.ReleaseDll.WithMetadataReferenceResolver( - new AssemblyReferenceResolver(new MetadataFileReferenceResolver(ImmutableArray.Create(), null), MetadataFileReferenceProvider.Default))); + new AssemblyReferenceResolver(MetadataFileReferenceResolver.Default, MetadataFileReferenceProvider.Default))); Assert.False(c1.ReferenceManagerEquals(c2)); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/CompilationCreationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/CompilationCreationTests.cs index e6db91e0baf9c11cc63e558a36386e1327b79fbf..1533098eee59eda24f9551ef2a8fb34d39c1f4f8 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/CompilationCreationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/CompilationCreationTests.cs @@ -2621,10 +2621,12 @@ public void AddRemoveReferences() private sealed class Resolver : TestMetadataReferenceResolver { + private readonly RelativePathReferenceResolver _pathResolver; private readonly string _data, _core, _system; public Resolver(string data, string core, string system) { + _pathResolver = RelativePathReferenceResolver.Default; _data = data; _core = core; _system = system; @@ -2644,7 +2646,7 @@ public override string ResolveReference(string reference, string baseFileName) return _system; default: - return base.ResolveReference(reference, baseFileName); + return _pathResolver.ResolveReference(reference, baseFileName); } } } diff --git a/src/Compilers/Core/CodeAnalysisTest/MetadataFileReferenceResolverTests.cs b/src/Compilers/Core/CodeAnalysisTest/MetadataFileReferenceResolverTests.cs index 62834b6df012c39363da9e09e8e0c560551ce299..37c522978b47f8398a367319d3e04648ee192286 100644 --- a/src/Compilers/Core/CodeAnalysisTest/MetadataFileReferenceResolverTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/MetadataFileReferenceResolverTests.cs @@ -163,7 +163,7 @@ public void ResolvePath_Order() var f1 = dir1.CreateFile("f.dll").Path; var f2 = dir2.CreateFile("f.dll").Path; - var resolver = new MetadataFileReferenceResolver( + var resolver = new RelativePathReferenceResolver( ImmutableArray.Create(dir1.Path, dir2.Path), baseDirectory: null); diff --git a/src/Compilers/Core/Portable/CodeAnalysis.csproj b/src/Compilers/Core/Portable/CodeAnalysis.csproj index 83fd0cd887003abb0e17a5612ae4ac968bdd9c16..d52b6ebf204300af13bdd701b455389a4662fcf7 100644 --- a/src/Compilers/Core/Portable/CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/CodeAnalysis.csproj @@ -390,6 +390,7 @@ + diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs b/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs index e5de96a598f1bc9c416d8df4a3ee31a7aaab1108..a4ad397ea19f3d5365907b315bbea1627427ef65 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs @@ -392,14 +392,10 @@ internal ImmutableArray ResolveAnalyzersFromArguments(string private AnalyzerFileReference ResolveAnalyzerReference(CommandLineAnalyzerReference reference, IAnalyzerAssemblyLoader analyzerLoader) { string resolvedPath = FileUtilities.ResolveRelativePath(reference.FilePath, basePath: null, baseDirectory: BaseDirectory, searchPaths: ReferencePaths, fileExists: PortableShim.File.Exists); - if (PortableShim.File.Exists(resolvedPath)) + if (resolvedPath != null) { resolvedPath = FileUtilities.TryNormalizeAbsolutePath(resolvedPath); } - else - { - resolvedPath = null; - } if (resolvedPath != null) { diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.ExistingReferencesResolver.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.ExistingReferencesResolver.cs index c50b35a1b472d482a88edf13b98a2924d22c0ce6..8d6dbedfd2f3893351aaacd96f4299d00ca65028 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.ExistingReferencesResolver.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.ExistingReferencesResolver.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.Linq; using Roslyn.Utilities; -using Microsoft.VisualStudio.Shell.Interop; namespace Microsoft.CodeAnalysis { @@ -16,25 +15,44 @@ internal abstract partial class CommonCompiler /// When scripts are included into a project we don't want #r's to reference other assemblies than those /// specified explicitly in the project references. /// - internal sealed class ExistingReferencesResolver : LoggingMetadataReferencesResolver + internal sealed class ExistingReferencesResolver : MetadataFileReferenceResolver { + private readonly MetadataFileReferenceResolver _resolver; private readonly ImmutableArray _availableReferences; private readonly AssemblyIdentityComparer _assemblyIdentityComparer; public ExistingReferencesResolver( + MetadataFileReferenceResolver resolver, ImmutableArray availableReferences, - ImmutableArray referencePaths, - string baseDirectory, - AssemblyIdentityComparer assemblyIdentityComparer, - TouchedFileLogger logger) - : base(referencePaths, baseDirectory, logger) + AssemblyIdentityComparer assemblyIdentityComparer) { Debug.Assert(!availableReferences.Any(r => r.Properties.Kind != MetadataImageKind.Assembly)); + _resolver = resolver; _availableReferences = availableReferences; _assemblyIdentityComparer = assemblyIdentityComparer; } + public override ImmutableArray SearchPaths + { + get { return _resolver.SearchPaths; } + } + + public override string BaseDirectory + { + get { return _resolver.BaseDirectory; } + } + + internal override MetadataFileReferenceResolver WithSearchPaths(ImmutableArray searchPaths) + { + return new ExistingReferencesResolver(_resolver.WithSearchPaths(searchPaths), _availableReferences, _assemblyIdentityComparer); + } + + internal override MetadataFileReferenceResolver WithBaseDirectory(string baseDirectory) + { + return new ExistingReferencesResolver(_resolver.WithBaseDirectory(baseDirectory), _availableReferences, _assemblyIdentityComparer); + } + public override string ResolveReference(string reference, string baseFilePath) { if (PathUtilities.IsFilePath(reference)) @@ -69,7 +87,7 @@ private string ResolveAssemblyName(string displayName) /// private string ResolveMetadataFile(string path, string basePath) { - var fullPath = base.ResolveReference(path, basePath); + var fullPath = _resolver.ResolveReference(path, basePath); foreach (var fileReference in _availableReferences) { diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.LoggingMetadataFileResolver.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.LoggingMetadataFileResolver.cs index ae72830ef9e92879d1147b80b64c31fb8a948b9a..e0814cee5ad02db72652e558ccc5e9d1a9104b26 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.LoggingMetadataFileResolver.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.LoggingMetadataFileResolver.cs @@ -1,29 +1,52 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Immutable; +using System.Diagnostics; namespace Microsoft.CodeAnalysis { internal abstract partial class CommonCompiler { - internal class LoggingMetadataReferencesResolver : MetadataFileReferenceResolver + internal sealed class LoggingMetadataReferencesResolver : MetadataFileReferenceResolver { - protected readonly TouchedFileLogger logger; + private readonly MetadataFileReferenceResolver _resolver; + private readonly TouchedFileLogger _logger; - public LoggingMetadataReferencesResolver(ImmutableArray searchPaths, string baseDirectory, TouchedFileLogger logger) - : base(searchPaths, baseDirectory) + public LoggingMetadataReferencesResolver(MetadataFileReferenceResolver resolver, TouchedFileLogger logger) { - this.logger = logger; + Debug.Assert(logger != null); + _resolver = resolver; + _logger = logger; } - protected override bool FileExists(string fullPath) + public override ImmutableArray SearchPaths { - if (logger != null && fullPath != null) + get { return _resolver.SearchPaths; } + } + + public override string BaseDirectory + { + get { return _resolver.BaseDirectory; } + } + + internal override MetadataFileReferenceResolver WithSearchPaths(ImmutableArray searchPaths) + { + return new LoggingMetadataReferencesResolver(_resolver.WithSearchPaths(searchPaths), _logger); + } + + internal override MetadataFileReferenceResolver WithBaseDirectory(string baseDirectory) + { + return new LoggingMetadataReferencesResolver(_resolver.WithBaseDirectory(baseDirectory), _logger); + } + + public override string ResolveReference(string reference, string baseFilePath) + { + var path = _resolver.ResolveReference(reference, baseFilePath); + if (path != null) { - logger.AddRead(fullPath); + _logger.AddRead(path); } - - return base.FileExists(fullPath); + return path; } } } diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs index 216b6a16d9fb488eb76caa86514e8d2db68ebc90..8b2fde58e863595ea03a58deee726fefc1c95df9 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @@ -86,7 +86,7 @@ internal virtual MetadataFileReferenceProvider GetMetadataProvider() internal virtual MetadataFileReferenceResolver GetExternalMetadataResolver(TouchedFileLogger touchedFiles) { - return new LoggingMetadataReferencesResolver(Arguments.ReferencePaths, Arguments.BaseDirectory, touchedFiles); + return CreateLoggingMetadataResolver(touchedFiles); } /// @@ -111,16 +111,20 @@ internal virtual MetadataFileReferenceResolver GetExternalMetadataResolver(Touch { // when compiling into an assembly (csc/vbc) we only allow #r that match references given on command line: referenceDirectiveResolver = new ExistingReferencesResolver( + CreateLoggingMetadataResolver(touchedFiles), resolved.Where(r => r.Properties.Kind == MetadataImageKind.Assembly).OfType().AsImmutable(), - Arguments.ReferencePaths, - Arguments.BaseDirectory, - assemblyIdentityComparer, - touchedFiles); + assemblyIdentityComparer); } return resolved; } + private MetadataFileReferenceResolver CreateLoggingMetadataResolver(TouchedFileLogger logger) + { + MetadataFileReferenceResolver resolver = new RelativePathReferenceResolver(Arguments.ReferencePaths, Arguments.BaseDirectory); + return (logger == null) ? resolver : new LoggingMetadataReferencesResolver(resolver, logger); + } + /// /// Reads content of a source file. /// diff --git a/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs b/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs index 75b2357c365c4460242adb1d41f953cf8d4862f2..62a2fb134a7835c665e7ff1c77bbb2871e570b1a 100644 --- a/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs +++ b/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Runtime.InteropServices; namespace Roslyn.Utilities { @@ -34,7 +33,7 @@ internal static class FileUtilities /// Method that tests existence of a file. /// /// - /// The resolved path or null if the path can't be resolved. + /// The resolved path or null if the path can't be resolved or does not exist. /// internal static string ResolveRelativePath( string path, @@ -47,6 +46,7 @@ internal static class FileUtilities Debug.Assert(searchPaths != null); Debug.Assert(fileExists != null); + string combinedPath; var kind = PathUtilities.GetPathKind(path); if (kind == PathKind.Relative) { @@ -54,10 +54,9 @@ internal static class FileUtilities baseDirectory = GetBaseDirectory(basePath, baseDirectory); if (baseDirectory != null) { - string combinedPath = PathUtilities.CombinePathsUnchecked(baseDirectory, path); + combinedPath = PathUtilities.CombinePathsUnchecked(baseDirectory, path); Debug.Assert(PathUtilities.IsAbsolute(combinedPath)); - - if (fileExists == null || fileExists(combinedPath)) + if (fileExists(combinedPath)) { return combinedPath; } @@ -66,10 +65,9 @@ internal static class FileUtilities // try search paths: foreach (var searchPath in searchPaths) { - string combinedPath = PathUtilities.CombinePathsUnchecked(searchPath, path); - + combinedPath = PathUtilities.CombinePathsUnchecked(searchPath, path); Debug.Assert(PathUtilities.IsAbsolute(combinedPath)); - if (fileExists == null || fileExists(combinedPath)) + if (fileExists(combinedPath)) { return combinedPath; } @@ -78,7 +76,17 @@ internal static class FileUtilities return null; } - return ResolveRelativePath(kind, path, basePath, baseDirectory); + combinedPath = ResolveRelativePath(kind, path, basePath, baseDirectory); + if (combinedPath != null) + { + Debug.Assert(PathUtilities.IsAbsolute(combinedPath)); + if (fileExists(combinedPath)) + { + return combinedPath; + } + } + + return null; } internal static string ResolveRelativePath(string path, string baseDirectory) diff --git a/src/Compilers/Core/Portable/MetadataFileReferenceResolver.cs b/src/Compilers/Core/Portable/MetadataFileReferenceResolver.cs index d463fe1444f6d72b8f1fc849d6bb4138081c94b9..2efdd837f2c957f50ec295d905939d7d5508777f 100644 --- a/src/Compilers/Core/Portable/MetadataFileReferenceResolver.cs +++ b/src/Compilers/Core/Portable/MetadataFileReferenceResolver.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; -using System.IO; using System.Linq; using Roslyn.Utilities; @@ -13,43 +9,9 @@ namespace Microsoft.CodeAnalysis /// /// Resolves metadata references specified in source code (#r directives). /// - internal class MetadataFileReferenceResolver + internal abstract class MetadataFileReferenceResolver { - public static readonly MetadataFileReferenceResolver Default = new MetadataFileReferenceResolver(ImmutableArray.Empty, baseDirectory: null); - - private readonly ImmutableArray _searchPaths; - private readonly string _baseDirectory; - - /// - /// Initializes a new instance of the class. - /// - /// An ordered set of fully qualified - /// paths which are searched when resolving assembly names. - /// Directory used when resolving relative paths. - public MetadataFileReferenceResolver(ImmutableArray searchPaths, string baseDirectory) - { - ValidateSearchPaths(searchPaths, "searchPaths"); - - if (baseDirectory != null && PathUtilities.GetPathKind(baseDirectory) != PathKind.Absolute) - { - throw ExceptionUtilities.Unreachable; - ////throw new ArgumentException(CodeAnalysisResources.AbsolutePathExpected, "baseDirectory"); - } - - _searchPaths = searchPaths; - _baseDirectory = baseDirectory; - } - - /// - /// Initializes a new instance of the class. - /// - /// An ordered set of fully qualified - /// paths which are searched when resolving assembly names. - /// Directory used when resolving relative paths. - public MetadataFileReferenceResolver(IEnumerable searchPaths, string baseDirectory) - : this(searchPaths.AsImmutableOrNull(), baseDirectory) - { - } + internal static readonly RelativePathReferenceResolver Default = new RelativePathReferenceResolver(ImmutableArray.Empty, baseDirectory: null); internal static void ValidateSearchPaths(ImmutableArray paths, string argName) { @@ -59,7 +21,7 @@ internal static void ValidateSearchPaths(ImmutableArray paths, string ar ////throw new ArgumentNullException(argName); } - if (paths.Any(path => !PathUtilities.IsAbsolute(path))) + if (!paths.All(PathUtilities.IsAbsolute)) { throw ExceptionUtilities.Unreachable; ////throw new ArgumentException(CodeAnalysisResources.AbsolutePathExpected, argName); @@ -72,9 +34,9 @@ internal static void ValidateSearchPaths(ImmutableArray paths, string ar /// /// All search paths are absolute. /// - public ImmutableArray SearchPaths + public abstract ImmutableArray SearchPaths { - get { return _searchPaths; } + get; } /// @@ -89,11 +51,15 @@ public ImmutableArray SearchPaths /// /// Resolution of a relative path that needs the base directory fails if the base directory is null. /// - public string BaseDirectory + public abstract string BaseDirectory { - get { return _baseDirectory; } + get; } + internal abstract MetadataFileReferenceResolver WithSearchPaths(ImmutableArray searchPaths); + + internal abstract MetadataFileReferenceResolver WithBaseDirectory(string baseDirectory); + /// /// Resolves a metadata reference that is a path or an assembly name. /// @@ -105,52 +71,6 @@ public string BaseDirectory /// /// Normalized absolute path to the referenced file or null if it can't be resolved. /// - public virtual string ResolveReference(string reference, string baseFilePath) - { - string resolvedPath = FileUtilities.ResolveRelativePath(reference, baseFilePath, _baseDirectory, _searchPaths, FileExists); - if (!FileExists(resolvedPath)) - { - return null; - } - - return FileUtilities.TryNormalizeAbsolutePath(resolvedPath); - } - - internal string ResolveReferenceChecked(string reference, string baseFilePath) - { - string fullPath = ResolveReference(reference, baseFilePath); - if (fullPath != null && !PathUtilities.IsAbsolute(fullPath)) - { - throw ExceptionUtilities.Unreachable; - //// throw new InvalidOperationException(string.Format(CodeAnalysisResources.PathReturnedByResolveMetadataFileMustBeAbsolute, GetType().FullName, fullPath)); - } - - return fullPath; - } - - protected virtual bool FileExists(string fullPath) - { - Debug.Assert(fullPath == null || PathUtilities.IsAbsolute(fullPath)); - return PortableShim.File.Exists(fullPath); - } - - public override bool Equals(object obj) - { - // Explicitly check that we're not comparing against a derived type - if (obj == null || GetType() != obj.GetType()) - { - return false; - } - - var other = (MetadataFileReferenceResolver)obj; - return string.Equals(_baseDirectory, other._baseDirectory, StringComparison.Ordinal) && - _searchPaths.SequenceEqual(other._searchPaths, StringComparer.Ordinal); - } - - public override int GetHashCode() - { - return Hash.Combine(_baseDirectory != null ? StringComparer.Ordinal.GetHashCode(_baseDirectory) : 0, - Hash.CombineValues(_searchPaths, StringComparer.Ordinal)); - } + public abstract string ResolveReference(string reference, string baseFilePath); } } diff --git a/src/Compilers/Core/Portable/RelativePathReferenceResolver.cs b/src/Compilers/Core/Portable/RelativePathReferenceResolver.cs new file mode 100644 index 0000000000000000000000000000000000000000..f5cd67bf87450d70353ff3b5acfc12a6613c3b11 --- /dev/null +++ b/src/Compilers/Core/Portable/RelativePathReferenceResolver.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis +{ + internal sealed class RelativePathReferenceResolver : MetadataFileReferenceResolver + { + private readonly ImmutableArray _searchPaths; + private readonly string _baseDirectory; + private readonly Func _fileExists; + + /// + /// Initializes a new instance of the class. + /// + /// An ordered set of fully qualified + /// paths which are searched when resolving assembly names. + /// Directory used when resolving relative paths. + /// Method that tests existence of a file. + public RelativePathReferenceResolver(ImmutableArray searchPaths, string baseDirectory, Func fileExists = null) + { + ValidateSearchPaths(searchPaths, "searchPaths"); + + if (baseDirectory != null && PathUtilities.GetPathKind(baseDirectory) != PathKind.Absolute) + { + throw ExceptionUtilities.Unreachable; + ////throw new ArgumentException(CodeAnalysisResources.AbsolutePathExpected, "baseDirectory"); + } + + _searchPaths = searchPaths; + _baseDirectory = baseDirectory; + _fileExists = fileExists ?? FileExists; + } + + public override ImmutableArray SearchPaths + { + get { return _searchPaths; } + } + + public override string BaseDirectory + { + get { return _baseDirectory; } + } + + internal override MetadataFileReferenceResolver WithSearchPaths(ImmutableArray searchPaths) + { + return new RelativePathReferenceResolver(searchPaths, _baseDirectory, _fileExists); + } + + internal override MetadataFileReferenceResolver WithBaseDirectory(string baseDirectory) + { + return new RelativePathReferenceResolver(_searchPaths, baseDirectory, _fileExists); + } + + public override string ResolveReference(string reference, string baseFilePath) + { + string resolvedPath = FileUtilities.ResolveRelativePath(reference, baseFilePath, _baseDirectory, _searchPaths, _fileExists); + if (resolvedPath == null) + { + return null; + } + + return FileUtilities.TryNormalizeAbsolutePath(resolvedPath); + } + + private static bool FileExists(string fullPath) + { + Debug.Assert(fullPath != null); + Debug.Assert(PathUtilities.IsAbsolute(fullPath)); + return PortableShim.File.Exists(fullPath); + } + + public override bool Equals(object obj) + { + var other = obj as RelativePathReferenceResolver; + return (other != null) && + string.Equals(_baseDirectory, other._baseDirectory, StringComparison.Ordinal) && + _searchPaths.SequenceEqual(other._searchPaths, StringComparer.Ordinal); + } + + public override int GetHashCode() + { + return Hash.Combine(_baseDirectory != null ? StringComparer.Ordinal.GetHashCode(_baseDirectory) : 0, + Hash.CombineValues(_searchPaths, StringComparer.Ordinal)); + } + } +} diff --git a/src/Compilers/Core/Portable/SourceFileResolver.cs b/src/Compilers/Core/Portable/SourceFileResolver.cs index 39c8e5f2c100838db67b40e58ad95931f7a31b43..317979fd3714dcff2209847387987679ac4c1f90 100644 --- a/src/Compilers/Core/Portable/SourceFileResolver.cs +++ b/src/Compilers/Core/Portable/SourceFileResolver.cs @@ -59,8 +59,7 @@ public override string NormalizePath(string path, string baseFilePath) public override string ResolveReference(string path, string baseFilePath) { string resolvedPath = FileUtilities.ResolveRelativePath(path, baseFilePath, _baseDirectory, _searchPaths, FileExists); - - if (!FileExists(resolvedPath)) + if (resolvedPath == null) { return null; } diff --git a/src/Compilers/Core/VBCSCompiler/AnalyzerConsistencyChecker.cs b/src/Compilers/Core/VBCSCompiler/AnalyzerConsistencyChecker.cs index 2723b4a077daed38d2f416fcd35dfc7d13351919..3ef678f3fd2ff6fd90b68805826ffe41cba45bf9 100644 --- a/src/Compilers/Core/VBCSCompiler/AnalyzerConsistencyChecker.cs +++ b/src/Compilers/Core/VBCSCompiler/AnalyzerConsistencyChecker.cs @@ -44,7 +44,7 @@ private static bool CheckCore(string baseDirectory, IEnumerable(), fileExists: File.Exists); - if (File.Exists(resolvedPath)) + if (resolvedPath != null) { resolvedPath = FileUtilities.TryNormalizeAbsolutePath(resolvedPath); if (resolvedPath != null) diff --git a/src/Compilers/Helpers/GlobalAssemblyCacheHelpers/GacFileResolver.cs b/src/Compilers/Helpers/GlobalAssemblyCacheHelpers/GacFileResolver.cs index 218dbdc55548e2f87d85170fe45b9a52a2c62d59..3856a2a634547e46d2306f2a78f60262141ccf52 100644 --- a/src/Compilers/Helpers/GlobalAssemblyCacheHelpers/GacFileResolver.cs +++ b/src/Compilers/Helpers/GlobalAssemblyCacheHelpers/GacFileResolver.cs @@ -1,11 +1,9 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.Linq; using System.Reflection; -using Microsoft.CodeAnalysis; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Scripting @@ -14,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Scripting /// Extends MetadataFileReferenceResolver to enable resolution of assembly /// simple names in the GAC. /// - internal sealed class GacFileResolver : MetadataFileReferenceResolver + internal sealed class GacFileResolver { private readonly ImmutableArray _architectures; private readonly CultureInfo _preferredCulture; @@ -23,27 +21,19 @@ internal sealed class GacFileResolver : MetadataFileReferenceResolver /// A resolver that is configured to resolve against the GAC associated /// with the bitness of the currently executing process. /// - internal new static GacFileResolver Default = new GacFileResolver( - assemblySearchPaths: ImmutableArray.Empty, - baseDirectory: null, + internal static GacFileResolver Default = new GacFileResolver( architectures: GlobalAssemblyCache.CurrentArchitectures, preferredCulture: null); /// /// Constructs an instance of a /// - /// An ordered set of fully qualified - /// paths which are searched when resolving assembly names. - /// Directory used when resolving relative paths. /// Supported architectures used to filter GAC assemblies. /// A culture to use when choosing the best assembly from /// among the set filtered by public GacFileResolver( - IEnumerable assemblySearchPaths, - string baseDirectory, ImmutableArray architectures, CultureInfo preferredCulture) - : base(assemblySearchPaths, baseDirectory) { _architectures = architectures; _preferredCulture = preferredCulture; @@ -65,34 +55,29 @@ public CultureInfo PreferredCulture get { return _preferredCulture; } } - public override string ResolveReference(string reference, string baseFilePath) + public string ResolveReference(string reference) { if (PathUtilities.IsFilePath(reference)) { - return base.ResolveReference(reference, baseFilePath); + return null; } string path; GlobalAssemblyCache.ResolvePartialName(reference, out path, _architectures, this.PreferredCulture); - return FileExists(path) ? path : null; + return (path != null && PortableShim.File.Exists(path)) ? path : null; } public override bool Equals(object obj) { - if (!base.Equals(obj)) - { - return false; - } - - var other = (GacFileResolver)obj; - return _architectures.SequenceEqual(other._architectures) && + var other = obj as GacFileResolver; + return (other != null) && + _architectures.SequenceEqual(other._architectures) && _preferredCulture == other._preferredCulture; } public override int GetHashCode() { - return Hash.Combine(base.GetHashCode(), - Hash.Combine(_preferredCulture, Hash.CombineValues(_architectures))); + return Hash.Combine(_preferredCulture, Hash.CombineValues(_architectures)); } } } diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb index 6ae88d3dbf5f892377ca45b4ae406cac030f63b3..c1705dc806a97a1b61de54b836263e2baac7d87a 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb @@ -1439,7 +1439,7 @@ End Class Dim c1 = VisualBasicCompilation.Create("c", options:=TestOptions.ReleaseDll) Dim c2 = c1.WithOptions(TestOptions.ReleaseDll.WithMetadataReferenceResolver( - New AssemblyReferenceResolver(New MetadataFileReferenceResolver(ImmutableArray.Create(Of String)(), Nothing), MetadataFileReferenceProvider.Default))) + New AssemblyReferenceResolver(MetadataFileReferenceResolver.Default, MetadataFileReferenceProvider.Default))) Assert.False(c1.ReferenceManagerEquals(c2)) Dim c3 = c1.WithOptions(TestOptions.ReleaseDll.WithMetadataReferenceResolver(c1.Options.MetadataReferenceResolver)) diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/VisualBasicCompilationOptionsTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/VisualBasicCompilationOptionsTests.vb index fc5fd84449d1ac7b90b2bf07f3d8ff77115eb893..2c0db2063e5158c50b138c44ebbf38df4a0bfcf5 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/VisualBasicCompilationOptionsTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/VisualBasicCompilationOptionsTests.vb @@ -78,7 +78,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests TestProperty(Function(old, value) old.WithXmlReferenceResolver(value), Function(opt) opt.XmlReferenceResolver, New XmlFileResolver(Nothing)) TestProperty(Function(old, value) old.WithSourceReferenceResolver(value), Function(opt) opt.SourceReferenceResolver, New SourceFileResolver(ImmutableArray(Of String).Empty, Nothing)) - TestProperty(Function(old, value) old.WithMetadataReferenceResolver(value), Function(opt) opt.MetadataReferenceResolver, New AssemblyReferenceResolver(New MetadataFileReferenceResolver({}, Nothing), New MetadataFileReferenceProvider())) + TestProperty(Function(old, value) old.WithMetadataReferenceResolver(value), Function(opt) opt.MetadataReferenceResolver, New AssemblyReferenceResolver(MetadataFileReferenceResolver.Default, New MetadataFileReferenceProvider())) TestProperty(Function(old, value) old.WithAssemblyIdentityComparer(value), Function(opt) opt.AssemblyIdentityComparer, New DesktopAssemblyIdentityComparer(New AssemblyPortabilityPolicy())) TestProperty(Function(old, value) old.WithStrongNameProvider(value), Function(opt) opt.StrongNameProvider, New DesktopStrongNameProvider()) End Sub diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs index e1cc2c3320b828d45f49f3dfb38e2f9c2e87395d..dfe751f99103750a00cbaf533f517ab4cdb3704a 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; @@ -712,7 +713,7 @@ public void TestWithReferenceDirective() Expression", parseOptions: GetScriptOptions(), -compilationOptions: TestOptions.ReleaseDll.WithMetadataReferenceResolver(new AssemblyReferenceResolver(new MetadataFileReferenceResolver(Array.Empty(), null), MetadataFileReferenceProvider.Default)), +compilationOptions: TestOptions.ReleaseDll.WithMetadataReferenceResolver(new AssemblyReferenceResolver(MetadataFileReferenceResolver.Default, MetadataFileReferenceProvider.Default)), compareTokens: false); } diff --git a/src/Interactive/EditorFeatures/Core/Extensibility/Interactive/InteractiveEvaluator.cs b/src/Interactive/EditorFeatures/Core/Extensibility/Interactive/InteractiveEvaluator.cs index 62023f849441690f82c59b9d2a8e2c66adab722e..3ebcbc72f6b4170c61301321bd98efa06960f1f0 100644 --- a/src/Interactive/EditorFeatures/Core/Extensibility/Interactive/InteractiveEvaluator.cs +++ b/src/Interactive/EditorFeatures/Core/Extensibility/Interactive/InteractiveEvaluator.cs @@ -13,10 +13,12 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; +using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.FileSystem; using Microsoft.CodeAnalysis.Editor.Implementation.Interactive; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Interactive; +using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; @@ -24,10 +26,10 @@ using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.InteractiveWindow; using Microsoft.VisualStudio.InteractiveWindow.Commands; -using Microsoft.CodeAnalysis.Scripting; using Roslyn.Utilities; +using DesktopMetadataReferenceResolver = WORKSPACES::Microsoft.CodeAnalysis.Scripting.DesktopMetadataReferenceResolver; using GacFileResolver = WORKSPACES::Microsoft.CodeAnalysis.Scripting.GacFileResolver; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.FileSystem; +using NuGetPackageResolver = WORKSPACES::Microsoft.CodeAnalysis.Scripting.NuGetPackageResolver; namespace Microsoft.CodeAnalysis.Editor.Interactive { @@ -45,7 +47,7 @@ public abstract class InteractiveEvaluator : IInteractiveEvaluator, ICurrentWork private readonly InteractiveWorkspace _workspace; private IInteractiveWindow _currentWindow; private ImmutableHashSet _references; - private GacFileResolver _metadataReferenceResolver; + private MetadataFileReferenceResolver _metadataReferenceResolver; private ImmutableArray _sourceSearchPaths; private ProjectId _previousSubmissionProjectId; @@ -201,12 +203,7 @@ private void ProcessStarting(InteractiveHostOptions options) referencePaths = rspArguments.ReferencePaths; // the base directory for references specified in the .rsp file is the .rsp file directory: - var rspMetadataReferenceResolver = new GacFileResolver( - referencePaths, - baseDirectory: rspArguments.BaseDirectory, - architectures: GacFileResolver.Default.Architectures, // TODO (tomat) - preferredCulture: System.Globalization.CultureInfo.CurrentCulture); // TODO (tomat) - + var rspMetadataReferenceResolver = CreateFileResolver(referencePaths, rspArguments.BaseDirectory); var metadataProvider = metadataService.GetProvider(); // ignore unresolved references, they will be reported in the interactive window: @@ -234,12 +231,7 @@ private void ProcessStarting(InteractiveHostOptions options) } // reset search paths, working directory: - _metadataReferenceResolver = new GacFileResolver( - referencePaths, - baseDirectory: _initialWorkingDirectory, - architectures: GacFileResolver.Default.Architectures, // TODO (tomat) - preferredCulture: System.Globalization.CultureInfo.CurrentCulture); // TODO (tomat) - + _metadataReferenceResolver = CreateFileResolver(referencePaths, _initialWorkingDirectory); _sourceSearchPaths = InteractiveHost.Service.DefaultSourceSearchPaths; // create the first submission project in the workspace after reset: @@ -254,6 +246,16 @@ private Dispatcher Dispatcher get { return ((FrameworkElement)GetInteractiveWindow().TextView).Dispatcher; } } + private static MetadataFileReferenceResolver CreateFileResolver(ImmutableArray referencePaths, string baseDirectory) + { + return new DesktopMetadataReferenceResolver( + new RelativePathReferenceResolver(referencePaths, baseDirectory), + NuGetPackageResolver.Instance, + new GacFileResolver( + architectures: GacFileResolver.Default.Architectures, // TODO (tomat) + preferredCulture: System.Globalization.CultureInfo.CurrentCulture)); // TODO (tomat) + } + #endregion #region Workspace @@ -563,11 +565,9 @@ public void UpdateLocalPaths(string[] newReferenceSearchPaths, string[] newSourc var changed = false; if (newReferenceSearchPaths != null || newBaseDirectory != null) { - _metadataReferenceResolver = new GacFileResolver( + _metadataReferenceResolver = CreateFileResolver( (newReferenceSearchPaths == null) ? _metadataReferenceResolver.SearchPaths : newReferenceSearchPaths.AsImmutable(), - baseDirectory: newBaseDirectory ?? _metadataReferenceResolver.BaseDirectory, - architectures: GacFileResolver.Default.Architectures, // TODO (tomat) - preferredCulture: System.Globalization.CultureInfo.CurrentCulture); // TODO (tomat) + newBaseDirectory ?? _metadataReferenceResolver.BaseDirectory); changed = true; } diff --git a/src/Interactive/Features/Interactive/Core/InteractiveHost.Service.cs b/src/Interactive/Features/Interactive/Core/InteractiveHost.Service.cs index f1cc6b945c857f7b94578981be8068cf8a62c3f8..af32bd821a41d4f30e6e866f1e74ae42431d227d 100644 --- a/src/Interactive/Features/Interactive/Core/InteractiveHost.Service.cs +++ b/src/Interactive/Features/Interactive/Core/InteractiveHost.Service.cs @@ -69,6 +69,16 @@ internal TaskResult(ScriptOptions options, ScriptState state) this.Options = options; this.State = state; } + + internal TaskResult With(ScriptOptions options) + { + return new TaskResult(options, State); + } + + internal TaskResult With(ScriptState state) + { + return new TaskResult(Options, state); + } } private static readonly ImmutableArray s_systemNoShadowCopyDirectories = ImmutableArray.Create( @@ -299,7 +309,6 @@ private static string GenerateUniqueChannelLocalName() { var result = await ReportUnhandledExceptionIfAny(lastTask).ConfigureAwait(false); var options = result.Options; - var state = result.State; try { Directory.SetCurrentDirectory(baseDirectory); @@ -316,7 +325,7 @@ private static string GenerateUniqueChannelLocalName() { operation.Completed(null); } - return new TaskResult(options, state); + return result.With(options); } /// @@ -365,7 +374,6 @@ private async Task AddReferenceAsync(Task lastTask, Remo var result = await ReportUnhandledExceptionIfAny(lastTask).ConfigureAwait(false); var success = false; var options = result.Options; - var state = result.State; try { string fullPath = ResolveReferencePath(options, reference, baseFilePath: null); @@ -386,7 +394,7 @@ private async Task AddReferenceAsync(Task lastTask, Remo { operation.Completed(success); } - return new TaskResult(options, state); + return result.With(options); } /// @@ -505,7 +513,7 @@ private TaskResult CompleteExecution(TaskResult result, RemoteAsyncOperation ReportUnhandledExceptionIfAny(Task lastTask) @@ -574,7 +582,8 @@ public void SetTestObjectFormattingOptions() // The base directory for relative paths is the directory that contains the .rsp file. // Note that .rsp files included by this .rsp file will share the base directory (Dev10 behavior of csc/vbc). - var args = parser.Parse(new[] { "@" + initializationFileOpt }, Path.GetDirectoryName(initializationFileOpt), RuntimeEnvironment.GetRuntimeDirectory(), null /* TODO: pass a valid value*/); + var rspDirectory = Path.GetDirectoryName(initializationFileOpt); + var args = parser.Parse(new[] { "@" + initializationFileOpt }, rspDirectory, RuntimeEnvironment.GetRuntimeDirectory(), null /* TODO: pass a valid value*/); foreach (var error in args.Errors) { @@ -586,6 +595,10 @@ public void SetTestObjectFormattingOptions() { // TODO (tomat): other arguments // TODO (tomat): parse options + var referencePaths = args.ReferencePaths; + result = result.With(result.Options.AddSearchPaths(referencePaths)); + _hostObject.ReferencePaths.Clear(); + _hostObject.ReferencePaths.AddRange(referencePaths); // TODO (tomat): consolidate with other reference resolving foreach (CommandLineReference cmdLineReference in args.MetadataReferences) @@ -596,10 +609,9 @@ public void SetTestObjectFormattingOptions() var options = result.Options; string fullPath = ResolveReferencePath(options, cmdLineReference.Reference, baseFilePath: null); LoadReference(fullPath, suppressWarnings: true, addReference: true, options: ref options); - result = new TaskResult(options, result.State); + result = result.With(options); } - var rspDirectory = Path.GetDirectoryName(initializationFileOpt); foreach (CommandLineSourceFile file in args.SourceFiles) { // execute all files as scripts (matches csi/vbi semantics) @@ -703,14 +715,13 @@ public SerializableAssemblyLoadResult LoadReferenceThrowing(string reference, bo { var result = ReportUnhandledExceptionIfAny(_lastTask).Result; var options = result.Options; - var state = result.State; var fullPath = ResolveReferencePath(options, reference, baseFilePath: null); if (fullPath == null) { throw new FileNotFoundException(message: null, fileName: reference); } var loadResult = LoadFromPathThrowing(fullPath, addReference, ref options); - _lastTask = Task.FromResult(new TaskResult(options, state)); + _lastTask = Task.FromResult(result.With(options)); return loadResult; } } @@ -874,12 +885,11 @@ private async Task ExecuteOnUIThread(TaskResult result, Script Image; } - private CompiledFile CompileLibrary(TempDirectory dir, string fileName, string assemblyName, string source, params MetadataReference[] references) + private static CompiledFile CompileLibrary(TempDirectory dir, string fileName, string assemblyName, string source, params MetadataReference[] references) { - const string Prefix = "RoslynTestFile_"; - - fileName = Prefix + fileName; - assemblyName = Prefix + assemblyName; - var file = dir.CreateFile(fileName); var compilation = CreateCompilation( new[] { source }, @@ -742,7 +737,7 @@ public void AddReference_MultipleReferencesWithSameWeakIdentity() //// var task = Host.InitializeContextAsync(rspFile.Path, isRestarting: false, killProcess: true); //// task.Wait(); - //// var output = ReadOutputToEnd().Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); + //// var output = SplitLines(ReadOutputToEnd()); //// var errorOutput = ReadErrorOutputToEnd(); //// Assert.Equal(4, output.Length); @@ -755,7 +750,7 @@ public void AddReference_MultipleReferencesWithSameWeakIdentity() //// Host.InitializeContextAsync(rspFile.Path).Wait(); - //// output = ReadOutputToEnd().Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); + //// output = SplitLines(ReadOutputToEnd()); //// errorOutput = ReadErrorOutputToEnd(); //// Assert.True(2 == output.Length, "Output is: '" + string.Join("", output) + "'. Expecting 2 lines."); @@ -793,7 +788,7 @@ public void AddReference_MultipleReferencesWithSameWeakIdentity() //// var errorOutput = ReadErrorOutputToEnd(); //// Assert.Equal("", errorOutput); - //// var output = ReadOutputToEnd().Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); + //// var output = SplitLines(ReadOutputToEnd()); //// Assert.Equal(4, output.Length); //// Assert.Equal("Microsoft (R) Roslyn C# Compiler version " + FileVersionInfo.GetVersionInfo(Host.GetType().Assembly.Location).FileVersion, output[0]); //// Assert.Equal("Loading context from '" + Path.GetFileName(rspFile.Path) + "'.", output[1]); @@ -801,6 +796,25 @@ public void AddReference_MultipleReferencesWithSameWeakIdentity() //// Assert.Equal("13", output[3]); //// } + [Fact] + public void ReferencePaths() + { + var directory = Temp.CreateDirectory(); + var assemblyName = GetUniqueName(); + CompileLibrary(directory, assemblyName + ".dll", assemblyName, @"public class C { }"); + var rspFile = Temp.CreateFile(); + rspFile.WriteAllText("/rp:" + directory.Path); + var task = Host.ResetAsync(InteractiveHostOptions.Default.WithInitializationFile(rspFile.Path)); + task.Wait(); + Execute( +$@"#r ""{assemblyName}.dll"" +typeof(C).Assembly.GetName()"); + var output = SplitLines(ReadOutputToEnd()); + Assert.Equal(2, output.Length); + Assert.Equal("Loading context from '" + Path.GetFileName(rspFile.Path) + "'.", output[0]); + Assert.Equal($"[{assemblyName}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]", output[1]); + } + [Fact] public void ReferenceDirectives() { @@ -972,5 +986,10 @@ public void SubmissionResult_PrintingVoid() } #endregion + + private static ImmutableArray SplitLines(string text) + { + return ImmutableArray.Create(text.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)); + } } } diff --git a/src/Interactive/csi/Csi.cs b/src/Interactive/csi/Csi.cs index d0375cf8f8619e00eb4dd54ee176c3018bb7db53..094e4bbd5b1a8554291da09c67c5cba1d2a70867 100644 --- a/src/Interactive/csi/Csi.cs +++ b/src/Interactive/csi/Csi.cs @@ -7,11 +7,9 @@ using System.Reflection; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.VisualStudio.Shell.Interop; using Microsoft.CodeAnalysis.Scripting; -using Roslyn.Utilities; +using Microsoft.VisualStudio.Shell.Interop; namespace CSharpInteractive { @@ -41,7 +39,10 @@ internal static int Main(string[] args) internal override MetadataFileReferenceResolver GetExternalMetadataResolver(TouchedFileLogger touchedFiles) { // We don't log touched files atm. - return new GacFileResolver(Arguments.ReferencePaths, Arguments.BaseDirectory, GacFileResolver.Default.Architectures, CultureInfo.CurrentCulture); + return new DesktopMetadataReferenceResolver( + new RelativePathReferenceResolver(Arguments.ReferencePaths, Arguments.BaseDirectory), + null, + new GacFileResolver(GacFileResolver.Default.Architectures, CultureInfo.CurrentCulture)); } public override void PrintLogo(TextWriter consoleOutput) diff --git a/src/Interactive/vbi/Vbi.vb b/src/Interactive/vbi/Vbi.vb index fc4171ad2751459dcbdfe748a5cf16a3c156b9bc..25d74a1122de9c0e5caaa7d35c96e08b14949519 100644 --- a/src/Interactive/vbi/Vbi.vb +++ b/src/Interactive/vbi/Vbi.vb @@ -5,9 +5,9 @@ Imports System.IO Imports System.Reflection Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Scripting Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.VisualStudio.Shell.Interop -Imports Microsoft.CodeAnalysis.Scripting Friend NotInheritable Class Vbi Inherits VisualBasicCompiler @@ -30,7 +30,10 @@ Friend NotInheritable Class Vbi Friend Overrides Function GetExternalMetadataResolver(touchedFiles As TouchedFileLogger) As MetadataFileReferenceResolver ' We don't log touched files atm. - Return New GacFileResolver(Arguments.ReferencePaths, Arguments.BaseDirectory, GacFileResolver.Default.Architectures, CultureInfo.CurrentCulture) + Return New DesktopMetadataReferenceResolver( + New RelativePathReferenceResolver(Arguments.ReferencePaths, Arguments.BaseDirectory), + Nothing, + New GacFileResolver(GacFileResolver.Default.Architectures, CultureInfo.CurrentCulture)) End Function Public Overrides Sub PrintLogo(consoleOutput As TextWriter) diff --git a/src/Scripting/Core/DesktopMetadataReferenceResolver.cs b/src/Scripting/Core/DesktopMetadataReferenceResolver.cs new file mode 100644 index 0000000000000000000000000000000000000000..5631f116721fbfaab550874c5cd09d6215077a3f --- /dev/null +++ b/src/Scripting/Core/DesktopMetadataReferenceResolver.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Scripting +{ + internal sealed class DesktopMetadataReferenceResolver : MetadataFileReferenceResolver + { + private readonly MetadataFileReferenceResolver _pathResolver; + private readonly NuGetPackageResolver _packageResolver; + private readonly GacFileResolver _gacFileResolver; + + internal DesktopMetadataReferenceResolver( + MetadataFileReferenceResolver pathResolver, + NuGetPackageResolver packageResolver, + GacFileResolver gacFileResolver) + { + _pathResolver = pathResolver; + _packageResolver = packageResolver; + _gacFileResolver = gacFileResolver; + } + + public override ImmutableArray SearchPaths + { + get { return _pathResolver.SearchPaths; } + } + + public override string BaseDirectory + { + get { return _pathResolver.BaseDirectory; } + } + + internal override MetadataFileReferenceResolver WithSearchPaths(ImmutableArray searchPaths) + { + return new DesktopMetadataReferenceResolver(_pathResolver.WithSearchPaths(searchPaths), _packageResolver, _gacFileResolver); + } + + internal override MetadataFileReferenceResolver WithBaseDirectory(string baseDirectory) + { + return new DesktopMetadataReferenceResolver(_pathResolver.WithBaseDirectory(baseDirectory), _packageResolver, _gacFileResolver); + } + + public override string ResolveReference(string reference, string baseFilePath) + { + if (PathUtilities.IsFilePath(reference)) + { + return _pathResolver.ResolveReference(reference, baseFilePath); + } + + if (_packageResolver != null) + { + string path = _packageResolver.ResolveNuGetPackage(reference); + if (path != null && PortableShim.File.Exists(path)) + { + return path; + } + } + + if (_gacFileResolver != null) + { + return _gacFileResolver.ResolveReference(reference); + } + + return null; + } + + public override bool Equals(object obj) + { + var other = obj as DesktopMetadataReferenceResolver; + return (other != null) && + object.Equals(_pathResolver, other._pathResolver) && + object.Equals(_packageResolver, other._packageResolver) && + object.Equals(_gacFileResolver, other._gacFileResolver); + } + + public override int GetHashCode() + { + int result = _pathResolver.GetHashCode(); + if (_packageResolver != null) + { + result = Hash.Combine(result, _packageResolver.GetHashCode()); + } + if (_gacFileResolver != null) + { + result = Hash.Combine(result, _gacFileResolver.GetHashCode()); + } + return result; + } + } +} diff --git a/src/Scripting/Core/NuGetPackageResolver.cs b/src/Scripting/Core/NuGetPackageResolver.cs new file mode 100644 index 0000000000000000000000000000000000000000..3cdb2ccd54adf23e3a8457dcaf8a6d312c2f3b71 --- /dev/null +++ b/src/Scripting/Core/NuGetPackageResolver.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Scripting +{ + internal sealed class NuGetPackageResolver + { + internal static readonly NuGetPackageResolver Instance = new NuGetPackageResolver(); + + private NuGetPackageResolver() + { + } + + internal string ResolveNuGetPackage(string reference) + { + if (PathUtilities.IsFilePath(reference)) + { + return null; + } + + var assemblyName = GetPackageAssemblyName(reference); + if (assemblyName == null) + { + return null; + } + + // Expecting {package}{version}\lib\{arch}\{package}.dll. + var resolvedPath = PathUtilities.CombineAbsoluteAndRelativePaths(reference, "lib"); + if (!PortableShim.Directory.Exists(resolvedPath)) + { + return null; + } + + // We're not validating the architecture currently + // so fail if there's not exactly one architecture. + resolvedPath = PortableShim.Directory.EnumerateDirectories(resolvedPath, "*", PortableShim.SearchOption.TopDirectoryOnly).SingleOrDefault(); + if (resolvedPath == null) + { + return null; + } + + return PathUtilities.CombineAbsoluteAndRelativePaths(resolvedPath, assemblyName); + } + + private static string GetPackageAssemblyName(string reference) + { + // Assembly name is in .nuspec file. + // For now, simply strip off any version #, etc. + var name = PathUtilities.GetFileName(reference); + int offset = 0; + while ((offset = name.IndexOf('.', offset)) >= 0) + { + if ((offset < name.Length - 1) && char.IsDigit(name[offset + 1])) + { + return name.Substring(0, offset) + ".dll"; + } + offset += 1; + } + return null; + } + } +} diff --git a/src/Scripting/Core/ScriptOptions.cs b/src/Scripting/Core/ScriptOptions.cs index adad6ece7f673f39d151611914e1a7af72b24608..fc5e407426358c7b729b52a401ab30bd5984043e 100644 --- a/src/Scripting/Core/ScriptOptions.cs +++ b/src/Scripting/Core/ScriptOptions.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.Scripting /// /// Options for creating and running scripts. /// - public class ScriptOptions + public sealed class ScriptOptions { private readonly ImmutableArray _references; private readonly ImmutableArray _namespaces; @@ -23,7 +23,12 @@ public class ScriptOptions public ScriptOptions() : this(ImmutableArray.Empty, ImmutableArray.Empty, - new AssemblyReferenceResolver(GacFileResolver.Default, MetadataFileReferenceProvider.Default), + new AssemblyReferenceResolver( + new DesktopMetadataReferenceResolver( + MetadataFileReferenceResolver.Default, + null, + GacFileResolver.Default), + MetadataFileReferenceProvider.Default), isInteractive: true) { } @@ -342,25 +347,10 @@ public ScriptOptions WithSearchPaths(IEnumerable searchPaths) else { // TODO: - var gacResolver = _referenceResolver.PathResolver as GacFileResolver; - if (gacResolver != null) - { - return With(resolver: new AssemblyReferenceResolver( - new GacFileResolver( - searchPaths, - gacResolver.BaseDirectory, - gacResolver.Architectures, - gacResolver.PreferredCulture), - _referenceResolver.Provider)); - } - else - { - return With(resolver: new AssemblyReferenceResolver( - new MetadataFileReferenceResolver( - searchPaths, - _referenceResolver.PathResolver.BaseDirectory), - _referenceResolver.Provider)); - } + var resolver = new AssemblyReferenceResolver( + _referenceResolver.PathResolver.WithSearchPaths(searchPaths.AsImmutableOrEmpty()), + _referenceResolver.Provider); + return With(resolver: resolver); } } @@ -407,25 +397,10 @@ public ScriptOptions WithBaseDirectory(string baseDirectory) else { // TODO: - var gacResolver = _referenceResolver.PathResolver as GacFileResolver; - if (gacResolver != null) - { - return With(resolver: new AssemblyReferenceResolver( - new GacFileResolver( - _referenceResolver.PathResolver.SearchPaths, - baseDirectory, - gacResolver.Architectures, - gacResolver.PreferredCulture), - _referenceResolver.Provider)); - } - else - { - return With(resolver: new AssemblyReferenceResolver( - new MetadataFileReferenceResolver( - _referenceResolver.PathResolver.SearchPaths, - baseDirectory), - _referenceResolver.Provider)); - } + var resolver = new AssemblyReferenceResolver( + _referenceResolver.PathResolver.WithBaseDirectory(baseDirectory), + _referenceResolver.Provider); + return With(resolver: resolver); } } @@ -434,7 +409,7 @@ public ScriptOptions WithBaseDirectory(string baseDirectory) /// internal ScriptOptions WithReferenceResolver(MetadataFileReferenceResolver resolver) { - if (resolver == _referenceResolver.PathResolver) + if (resolver.Equals(_referenceResolver.PathResolver)) { return this; } @@ -447,7 +422,7 @@ internal ScriptOptions WithReferenceResolver(MetadataFileReferenceResolver resol /// internal ScriptOptions WithReferenceProvider(MetadataFileReferenceProvider provider) { - if (provider == _referenceResolver.Provider) + if (provider.Equals(_referenceResolver.Provider)) { return this; } diff --git a/src/Scripting/Core/Scripting.csproj b/src/Scripting/Core/Scripting.csproj index 8c72e0faa6bf89082bfe66d03e973df541191b71..d158b8b79a2cf2bc0a0ff63b4464f59a1b005953 100644 --- a/src/Scripting/Core/Scripting.csproj +++ b/src/Scripting/Core/Scripting.csproj @@ -147,9 +147,11 @@ GlobalAssemblyCache.cs + + @@ -203,4 +205,4 @@ - + \ No newline at end of file diff --git a/src/Scripting/Test/Session.cs b/src/Scripting/Test/Session.cs index 3373d505bad95a84a76d610c1385f5e93eabeda7..e3b87d085332fa348370bc55409c9246b1630401 100644 --- a/src/Scripting/Test/Session.cs +++ b/src/Scripting/Test/Session.cs @@ -40,14 +40,6 @@ public ScriptEngine Engine get { return _engine; } } - internal MetadataFileReferenceResolver MetadataReferenceResolver - { - get - { - return _options.AssemblyResolver.PathResolver; - } - } - internal Type HostObjectType { get { return _globalsType; } diff --git a/src/Test/Utilities/TestMetadataReferenceResolver.cs b/src/Test/Utilities/TestMetadataReferenceResolver.cs index 2db1d49a86c0e68f065a3ed1d8422a034ddbcc45..7d24aecac88b7dc55db10e350c7e41cce61c79d8 100644 --- a/src/Test/Utilities/TestMetadataReferenceResolver.cs +++ b/src/Test/Utilities/TestMetadataReferenceResolver.cs @@ -14,10 +14,29 @@ namespace Roslyn.Test.Utilities /// internal abstract class TestMetadataReferenceResolver : MetadataFileReferenceResolver { - public TestMetadataReferenceResolver() - : base(searchPaths: ImmutableArray.Create(), - baseDirectory: null) + public override string ResolveReference(string reference, string baseFilePath) + { + return null; + } + + public override ImmutableArray SearchPaths { + get { return ImmutableArray.Empty; } + } + + public override string BaseDirectory + { + get { return null; } + } + + internal override MetadataFileReferenceResolver WithSearchPaths(ImmutableArray searchPaths) + { + throw new NotImplementedException(); + } + + internal override MetadataFileReferenceResolver WithBaseDirectory(string baseDirectory) + { + throw new NotImplementedException(); } } @@ -52,22 +71,48 @@ public override string ResolveReference(string reference, string baseFilePath) } } - internal class VirtualizedFileReferenceResolver : MetadataFileReferenceResolver + internal sealed class VirtualizedFileReferenceResolver : MetadataFileReferenceResolver { + private readonly RelativePathReferenceResolver _resolver; private readonly HashSet _existingFullPaths; public VirtualizedFileReferenceResolver( IEnumerable existingFullPaths = null, string baseDirectory = null, ImmutableArray searchPaths = default(ImmutableArray)) - : base(searchPaths.NullToEmpty(), baseDirectory) { + _resolver = new RelativePathReferenceResolver(searchPaths.NullToEmpty(), baseDirectory, FileExists); _existingFullPaths = new HashSet(existingFullPaths, StringComparer.OrdinalIgnoreCase); } - protected override bool FileExists(string fullPath) + public override ImmutableArray SearchPaths + { + get { return _resolver.SearchPaths; } + } + + public override string BaseDirectory + { + get { return _resolver.BaseDirectory; } + } + + internal override MetadataFileReferenceResolver WithSearchPaths(ImmutableArray searchPaths) + { + throw new NotImplementedException(); + } + + internal override MetadataFileReferenceResolver WithBaseDirectory(string baseDirectory) + { + throw new NotImplementedException(); + } + + public override string ResolveReference(string reference, string baseFilePath) + { + return _resolver.ResolveReference(reference, baseFilePath); + } + + private bool FileExists(string fullPath) { - return fullPath != null && _existingFullPaths != null && _existingFullPaths.Contains(FileUtilities.NormalizeAbsolutePath(fullPath)); + return _existingFullPaths.Contains(FileUtilities.NormalizeAbsolutePath(fullPath)); } } } diff --git a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.cs b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.cs index c1b241dcdfb298ab87d8a76d58756da9c3bcd1b6..e5b5719f7e5df01bc569226cb096e12f81ad3e01 100644 --- a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.cs +++ b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.cs @@ -183,7 +183,7 @@ protected override CSharpCompilationOptions CreateCompilationOptions() if (this.Workspace != null) { referenceResolver = new AssemblyReferenceResolver( - new MetadataFileReferenceResolver(referenceSearchPaths, projectDirectory), + new RelativePathReferenceResolver(referenceSearchPaths, projectDirectory), this.Workspace.CurrentSolution.Services.MetadataService.GetProvider()); } else diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs index effcf80aaeee32e71b992e4b72ceab0b0dd2924c..c6765b8d7f9e980e925ac517ab30a0a979bcbfcc 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs @@ -1267,7 +1267,7 @@ private static MetadataFileReferenceResolver CreateMetadataReferenceResolver(str assemblySearchPaths = ImmutableArray.Create(outputDirectory); } - return new MetadataFileReferenceResolver(assemblySearchPaths, baseDirectory: projectDirectory); + return new RelativePathReferenceResolver(assemblySearchPaths, baseDirectory: projectDirectory); } private bool TryGetOutputPathFromBuildManager(out string binOutputPath) diff --git a/src/Workspaces/Core/Desktop/Workspace/CommandLineProject.cs b/src/Workspaces/Core/Desktop/Workspace/CommandLineProject.cs index c2c54d41c83595f7b025a15a5107165a598f9c43..2506a283301c0db23264c286bb729c5dc0e25b45 100644 --- a/src/Workspaces/Core/Desktop/Workspace/CommandLineProject.cs +++ b/src/Workspaces/Core/Desktop/Workspace/CommandLineProject.cs @@ -34,7 +34,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language, var commandLineArguments = commandLineParser.Parse(commandLineArgs, projectDirectory, isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); // TODO (tomat): to match csc.exe/vbc.exe we should use CommonCommandLineCompiler.ExistingReferencesResolver to deal with #r's - var referenceResolver = new MetadataFileReferenceResolver(commandLineArguments.ReferencePaths, commandLineArguments.BaseDirectory); + var referenceResolver = new RelativePathReferenceResolver(commandLineArguments.ReferencePaths, commandLineArguments.BaseDirectory); var referenceProvider = tmpWorkspace.Services.GetRequiredService().GetProvider(); var analyzerLoader = tmpWorkspace.Services.GetRequiredService().GetLoader(); var xmlFileResolver = new XmlFileResolver(commandLineArguments.BaseDirectory); diff --git a/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildProjectLoader.cs b/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildProjectLoader.cs index be933abc6e7408bcc94a5a6af021dd9f9d55282f..1b5bc32544ac74580768e335c9bc8056cc977f4b 100644 --- a/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildProjectLoader.cs +++ b/src/Workspaces/Core/Desktop/Workspace/MSBuild/MSBuildProjectLoader.cs @@ -361,7 +361,7 @@ private async Task LoadProjectAsync(string projectFilePath, IProjectF isInteractive: false, sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory()); - var resolver = new MetadataFileReferenceResolver(commandLineArgs.ReferencePaths, commandLineArgs.BaseDirectory); + var resolver = new RelativePathReferenceResolver(commandLineArgs.ReferencePaths, commandLineArgs.BaseDirectory); var metadataReferences = commandLineArgs.ResolveMetadataReferences(new AssemblyReferenceResolver(resolver, metadataService.GetProvider())); var analyzerLoader = analyzerService.GetLoader(); @@ -449,7 +449,7 @@ private async Task LoadProjectAsync(string projectFilePath, IProjectF .WithSourceReferenceResolver(new SourceFileResolver(ImmutableArray.Empty, projectDirectory)) .WithMetadataReferenceResolver( new AssemblyReferenceResolver( - new MetadataFileReferenceResolver(ImmutableArray.Empty, projectDirectory), + new RelativePathReferenceResolver(ImmutableArray.Empty, projectDirectory), MetadataFileReferenceProvider.Default)) .WithStrongNameProvider(new DesktopStrongNameProvider(ImmutableArray.Create(projectDirectory, outputFilePath))) .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default); diff --git a/src/Workspaces/Core/Desktop/Workspaces.Desktop.csproj b/src/Workspaces/Core/Desktop/Workspaces.Desktop.csproj index 24f23e7b3a92c2bfece13496e9b2866ce824c236..a201b29b4376209ccbca74f2e7abbddab455c574 100644 --- a/src/Workspaces/Core/Desktop/Workspaces.Desktop.csproj +++ b/src/Workspaces/Core/Desktop/Workspaces.Desktop.csproj @@ -86,6 +86,12 @@ InternalUtilities\SimpleAnalyzerAssemblyLoader.cs + + InternalUtilities\DesktopMetadataReferenceResolver.cs + + + InternalUtilities\NuGetPackageResolver.cs + diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 83dbc0ac96fd7542ee8c0ebf098aebf248652da3..bdb42bf924111ea86f1baf104da9874001bdec37 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -248,6 +248,9 @@ InternalUtilities\MetadataFileReferenceResolver.cs + + InternalUtilities\RelativePathReferenceResolver.cs + InternalUtilities\ReflectionUtilities.cs